T-SQL动态SQL和临时表

Cha*_*adD 17 t-sql sql-server stored-procedures dynamic-sql temp-tables

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

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

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

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

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

我希望做的是这样的:

在客户端上构造一个动态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 */
    "
Run Code Online (Sandbox Code Playgroud)

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

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)
Run Code Online (Sandbox Code Playgroud)

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

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

1)可以通过从客户端执行动态构造的SQL来执行第一个SQL.然后,结果可以作为表传递给上述存储过程的修改版本.我熟悉将表数据作为XML传递.如果我这样做,那么存储过程可以使用静态SQL将数据插入到临时表中,因为它是由动态SQL创建的,然后可以毫无问题地进行查询.(我还可以调查传递新的表类型参数而不是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)
Run Code Online (Sandbox Code Playgroud)

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

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

如果你甚至在我写的内容中扫描了这么远,我很惊讶,但如果是这样的话,我会对你如何做到最好的想法表示不满.

SQL*_*ace 25

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

这有效:

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

SELECT *
FROM #temp3
Run Code Online (Sandbox Code Playgroud)

这不起作用:

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

SELECT *
FROM #temp2
Run Code Online (Sandbox Code Playgroud)

换一种说法:

  1. 创建临时表
  2. 执行proc
  3. 从临时表中选择

这是完整的例子:

CREATE PROC prTest2 @var VARCHAR(100)
AS
EXEC (@var)
GO

CREATE TABLE #temp (id INT)

EXEC prTest2 'insert #temp values(1)'

SELECT *
FROM #temp
Run Code Online (Sandbox Code Playgroud)

  • 问题是当我们不知道列定义时,那怎么样? (13认同)

Sid*_*Sid 5

第一种方法-将多个语句包含在同一动态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
Run Code Online (Sandbox Code Playgroud)

第二种方法-使用全局温度表:(
小心,您需要格外注意全局变量。)

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

    SELECT *
    FROM ##temp2
END
Run Code Online (Sandbox Code Playgroud)

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

IF (OBJECT_ID('tempdb..##temp2') IS NOT NULL)
BEGIN
     DROP Table ##temp2
END
Run Code Online (Sandbox Code Playgroud)

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


Dav*_*ers 5

我遇到了@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
Run Code Online (Sandbox Code Playgroud)

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