T-SQL动态SQL和临时表

时间:2010-05-27 00:20:30

标签: sql-server tsql stored-procedures dynamic-sql temp-tables

看起来通过EXECUTE字符串方法使用动态SQL创建的#temptables具有不同的范围,并且在同一存储过程中不能被“固定”SQL引用。 但是,我可以在子序列动态SQL中引用由动态SQL语句创建的临时表,但似乎存储过程不会将查询结果返回给调用客户端,除非修复了SQL。

一个简单的2表场景: 我有2张桌子。我们称它们为订单和物品。 Order具有OrderId的主键,而Items具有ItemId的主键。 Items.OrderId是标识父订单的外键。订单可以包含1到n个项目。

我希望能够为用户提供一个非常灵活的“查询构建器”类型界面,以允许用户选择他想要查看的项目。过滤条件可以基于Items表和/或父Order表中的字段。如果一个Item符合过滤条件,包括父订单中的条件和条件(如果存在),那么该项应该在查询以及父订单中返回。

通常,我想,大多数人会在Item表和父Order表之间构建一个连接。我想改为执行2个单独的查询。一个返回所有符合条件的项目,另一个返回所有不同的父订单。原因有两个,你可能会也可能不会同意。

第一个原因是我需要查询父Order表中的所有列,如果我执行单个查询将Orders表连接到Items表,我将多次重新发布Order信息。由于每个订单通常有大量商品,我想避免这种情况,因为这会导致更多的数据被转移到胖客户端。相反,如上所述,我想在数据集中单独返回两个表,并使用其中的两个表来填充自定义Order和子Items客户端对象。 (我对LINQ或Entity Framework还不够了解。我手工构建了我的对象)。我想要返回两个表而不是一个表的第二个原因是因为我已经有另一个过程返回给定OrderId的所有Items以及父Order,我想使用相同的2-table方法以便我可以重用客户端代码从返回的2个数据表填充我的自定义Order和Client对象。

我希望这样做:

在客户端上构造一个动态SQL字符串,它将orders表连接到Items表,并根据在Winform胖客户端应用程序上创建的自定义过滤器指定的每个表上的过滤器。客户端上的SQL构建看起来像这样:

TempSQL = "

    INSERT INTO #ItemsToQuery
       OrderId, ItemsId
    FROM
       Orders, Items 
    WHERE
       Orders.OrderID = Items.OrderId AND
       /* Some unpredictable Order filters go here */
      AND
       /* Some unpredictable Items filters go here */
    "

然后,我会调用存储过程,

CREATE PROCEDURE GetItemsAndOrders(@tempSql as text)
   Execute (@tempSQL) --to create the #ItemsToQuery table

SELECT * FROM Items WHERE Items.ItemId IN (SELECT ItemId FROM #ItemsToQuery)

SELECT * FROM Orders WHERE Orders.OrderId IN (SELECT DISTINCT OrderId FROM #ItemsToQuery)

这种方法的问题在于,#ItemsToQuery表由于是由动态SQL创建的,因此无法从以下2个静态SQL中访问,如果我将静态SQL更改为动态,则不会将结果传递回胖客户端。

我想到了一个更好的想法:

1)可以通过从客户端执行动态构造的SQL来执行第一个SQL。然后,结果可以作为表传递给上述存储过程的修改版本。我熟悉将表数据作为XML传递。如果我这样做,那么存储过程可以使用静态SQL将数据插入到临时表中,因为它是由动态SQL创建的,然后可以毫无问题地进行查询。 (我还可以调查传递新的Table类型参数而不是XML。)但是,我想避免将可能较大的列表传递给存储过程。

2)我可以从客户端执行所有查询。

第一个是这样的:

SELECT Items.* FROM Orders, Items WHERE Order.OrderId = Items.OrderId AND (dynamic filter)
SELECT Orders.* FROM Orders, Items WHERE Order.OrderId = Items.OrderId AND (dynamic filter)

这仍然使我能够重用我的客户端对象 - 群体代码,因为Orders和Items继续在两个不同的表中返回。

我有一种感觉,我可能在我的存储过程中使用Table数据类型有一些选项,但这对我来说也是新的,我会很感激用一点勺子喂它。

如果你甚至在我所写的内容中进行了扫描,我很惊讶,但如果是这样的话,我会对你如何做到最好的想法感到失望。

5 个答案:

答案 0 :(得分:20)

首先需要先创建表,然后才能在动态SQL中使用它。

这有效:

CREATE TABLE #temp3 (id INT)
EXEC ('insert #temp3 values(1)')

SELECT *
FROM #temp3

这不起作用:

EXEC (
        'create table #temp2 (id int)
         insert #temp2 values(1)'
        )

SELECT *
FROM #temp2

换句话说:

  1. 创建临时表
  2. 执行proc
  3. 从临时表中选择
  4. 这是完整的例子:

    CREATE PROC prTest2 @var VARCHAR(100)
    AS
    EXEC (@var)
    GO
    
    CREATE TABLE #temp (id INT)
    
    EXEC prTest2 'insert #temp values(1)'
    
    SELECT *
    FROM #temp
    

答案 1 :(得分:4)

第一种方法 - 在同一个动态SQL调用中包含多个语句:

DECLARE @DynamicQuery NVARCHAR(MAX)

SET @DynamicQuery = 'Select * into #temp from (select * from tablename) alias 
select * from #temp
drop table #temp'

EXEC sp_executesql @DynamicQuery

第二种方法 - 使用全球临时表:
(小心,你需要特别注意全局变量。)

IF OBJECT_ID('tempdb..##temp2') IS NULL
BEGIN
    EXEC (
            'create table ##temp2 (id int)
             insert ##temp2 values(1)'
            )

    SELECT *
    FROM ##temp2
END

完成后,不要忘记手动删除## temp2对象:

IF (OBJECT_ID('tempdb..##temp2') IS NOT NULL)
BEGIN
     DROP Table ##temp2
END

注意:如果您不了解数据库的完整结构,请不要使用此方法。

答案 2 :(得分:2)

我强烈建议你仔细阅读http://www.sommarskog.se/arrays-in-sql-2005.html

我个人喜欢传递逗号分隔文本列表,然后用文本解析为表函数并加入它的方法。如果在连接中首先创建临时表方法,则可以使用临时表方法。但它感觉有点麻烦。

答案 3 :(得分:1)

我遇到了@Muflix提到的相同问题。当您不知道要返回的列或它们是动态生成的时,我要做的是创建一个具有唯一ID的全局表,然后在完成后将其删除,这看起来就像显示的一样下方:

DECLARE @DynamicSQL NVARCHAR(MAX)
DECLARE @DynamicTable VARCHAR(255) = 'DynamicTempTable_' + CONVERT(VARCHAR(36), NEWID())
DECLARE @DynamicColumns NVARCHAR(MAX)

--Get "@DynamicColumns", example: SET @DynamicColumns = '[Column1], [Column2]'

SET @DynamicSQL = 'SELECT ' + @DynamicColumns + ' INTO [##' + @DynamicTable + ']' + 
     ' FROM [dbo].[TableXYZ]'

EXEC sp_executesql @DynamicSQL

SET @DynamicSQL = 'IF OBJECT_ID(''tempdb..##' + @DynamicTable + ''' , ''U'') IS NOT NULL ' + 
    ' BEGIN DROP TABLE [##' + @DynamicTable + '] END'

EXEC sp_executesql @DynamicSQL

当然不是最好的解决方案,但这似乎对我有用。

答案 4 :(得分:0)

动态SQL的结果集将返回给客户端。我做了很多这样的事。

关于通过临时表和变量共享数据的问题,以及SQL生成的动态SQL之间的问题,你是对的。

我认为在尝试让你的临时表工作时,你可能会遇到一些困惑,因为你绝对可以从执行动态SQL的SP获取数据:

USE SandBox
GO

CREATE PROCEDURE usp_DynTest(@table_type AS VARCHAR(255))
AS 
BEGIN
    DECLARE @sql AS VARCHAR(MAX) = 'SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = ''' + @table_type + ''''
    EXEC (@sql)
END
GO

EXEC usp_DynTest 'BASE TABLE'
GO

EXEC usp_DynTest 'VIEW'
GO

DROP PROCEDURE usp_DynTest
GO

此外:

USE SandBox
GO

CREATE PROCEDURE usp_DynTest(@table_type AS VARCHAR(255))
AS 
BEGIN
    DECLARE @sql AS VARCHAR(MAX) = 'SELECT * INTO #temp FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = ''' + @table_type + '''; SELECT * FROM #temp;'
    EXEC (@sql)
END
GO

EXEC usp_DynTest 'BASE TABLE'
GO

EXEC usp_DynTest 'VIEW'
GO

DROP PROCEDURE usp_DynTest
GO
相关问题