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)
换一种说法:
这是完整的例子:
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)
第一种方法-将多个语句包含在同一动态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。
我遇到了@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)
当然不是最好的解决方案,但这似乎对我有用。