如何在dynamicSQL语句中使用表variables?

在我的存储过程中,我在过程之上声明了两个表variables。 现在我正在尝试在dynamicsql语句中使用该表variables,但在执行该过程时出现此错误。 我正在使用Sql Server 2008。

这是我的查询的样子,

set @col_name = 'Assoc_Item_' + Convert(nvarchar(2), @curr_row1); set @sqlstat = 'update @RelPro set ' + @col_name + ' = (Select relsku From @TSku Where tid = ' + Convert(nvarchar(2), @curr_row1) + ') Where RowID = ' + Convert(nvarchar(2), @curr_row); Exec(@sqlstat); 

我得到以下错误,

必须声明表variables“@RelPro”。 必须声明表variables“@TSku”。

我试图把表的dynamic查询的string块外,但无济于事。

您的EXEC执行在不同的上下文中,因此它不知道在原始上下文中已经声明了任何variables。 您应该能够使用临时表而不是表variables,如下面的简单演示所示。

 create table #t (id int) declare @value nchar(1) set @value = N'1' declare @sql nvarchar(max) set @sql = N'insert into #t (id) values (' + @value + N')' exec (@sql) select * from #t drop table #t 

在SQL Server 2008+上,只要不需要更新表本身的值,就可以使用表值参数将表variables传递给dynamicSQL语句。

所以从你发布的代码你可以使用这种方法@TSku而不是@RelPro

下面的示例语法。

 CREATE TYPE MyTable AS TABLE ( Foo int, Bar int ); GO DECLARE @T AS MyTable; INSERT INTO @T VALUES (1,2), (2,3) SELECT *, sys.fn_PhysLocFormatter(%%physloc%%) AS [physloc] FROM @T EXEC sp_executesql N'SELECT *, sys.fn_PhysLocFormatter(%%physloc%%) AS [physloc] FROM @T', N'@T MyTable READONLY', @T=@T 

包含physloc列只是为了说明在子范围中引用的表variables与外部范围相比是绝对相同的,而不是副本。

您不必使用dynamicSQL

 update R set Assoc_Item_1 = CASE WHEN @curr_row = 1 THEN foo.relsku ELSE Assoc_Item_1 END, Assoc_Item_2 = CASE WHEN @curr_row = 2 THEN foo.relsku ELSE Assoc_Item_2 END, Assoc_Item_3 = CASE WHEN @curr_row = 3 THEN foo.relsku ELSE Assoc_Item_3 END, Assoc_Item_4 = CASE WHEN @curr_row = 4 THEN foo.relsku ELSE Assoc_Item_4 END, Assoc_Item_5 = CASE WHEN @curr_row = 5 THEN foo.relsku ELSE Assoc_Item_5 END, ... from (Select relsku From @TSku Where tid = @curr_row1) foo CROSS JOIN @RelPro R Where R.RowID = @curr_row; 

你不能这样做,因为表variables超出了范围。

您将不得不在dynamicSQL语句内声明表variables或创build临时表。

我build议你阅读这篇关于dynamicSQL的优秀文章。

http://www.sommarskog.se/dynamic_sql.html

那么,我想出了办法,想和那些可能遇到同样问题的人分享。

让我从我一直面临的问题开始,

我一直在试图执行一个dynamicSql语句,它使用了我在我的存储过程的顶部声明的两个临时表,但是因为那个dynamic的SQL语句创build了一个新的作用域,所以我不能使用临时表。

解:

我只是将它们改为全局临时variables,他们工作。

在下面find我的存储过程。

 CREATE PROCEDURE RAFCustom_Room_GetRelatedProducts -- Add the parameters for the stored procedure here @PRODUCT_SKU nvarchar(15) = Null 

AS BEGIN – SET NOCOUNT ON添加以防止来自干涉SELECT语句的额外结果集。 SET NOCOUNT ON;

 IF OBJECT_ID('tempdb..##RelPro', 'U') IS NOT NULL BEGIN DROP TABLE ##RelPro END Create Table ##RelPro ( RowID int identity(1,1), ID int, Item_Name nvarchar(max), SKU nvarchar(max), Vendor nvarchar(max), Product_Img_180 nvarchar(max), rpGroup int, Assoc_Item_1 nvarchar(max), Assoc_Item_2 nvarchar(max), Assoc_Item_3 nvarchar(max), Assoc_Item_4 nvarchar(max), Assoc_Item_5 nvarchar(max), Assoc_Item_6 nvarchar(max), Assoc_Item_7 nvarchar(max), Assoc_Item_8 nvarchar(max), Assoc_Item_9 nvarchar(max), Assoc_Item_10 nvarchar(max) ); Begin Insert ##RelPro(ID, Item_Name, SKU, Vendor, Product_Img_180, rpGroup) Select distinct zp.ProductID, zp.Name, zp.SKU, (Select m.Name From ZNodeManufacturer m(nolock) Where m.ManufacturerID = zp.ManufacturerID), 'http://s0001.server.com/is/sw11/DG/' + (Select m.Custom1 From ZNodeManufacturer m(nolock) Where m.ManufacturerID = zp.ManufacturerID) + '_' + zp.SKU + '_3?$SC_3243$', ep.RoomID From Product zp(nolock) Inner Join RF_ExtendedProduct ep(nolock) On ep.ProductID = zp.ProductID Where zp.ActiveInd = 1 And SUBSTRING(zp.SKU, 1, 2) <> 'GC' AND zp.Name <> 'PLATINUM' AND zp.SKU = (Case When @PRODUCT_SKU Is Not Null Then @PRODUCT_SKU Else zp.SKU End) End declare @curr_row int = 0, @tot_rows int= 0, @sku nvarchar(15) = null; IF OBJECT_ID('tempdb..##TSku', 'U') IS NOT NULL BEGIN DROP TABLE ##TSku END Create Table ##TSku (tid int identity(1,1), relsku nvarchar(15)); Select @curr_row = (Select MIN(RowId) From ##RelPro); Select @tot_rows = (Select MAX(RowId) From ##RelPro); while @curr_row <= @tot_rows Begin select @sku = SKU from ##RelPro where RowID = @curr_row; truncate table ##TSku; Insert ##TSku(relsku) Select distinct top(10) tzp.SKU From Product tzp(nolock) INNER JOIN [INTRANET].raf_FocusAssociatedItem assoc(nolock) ON assoc.associatedItemID = tzp.SKU Where (assoc.isActive=1) And (tzp.ActiveInd = 1) AND (assoc.productID = @sku) declare @curr_row1 int = (Select Min(tid) From ##TSku), @tot_rows1 int = (Select Max(tid) From ##TSku); If(@tot_rows1 <> 0) Begin While @curr_row1 <= @tot_rows1 Begin declare @col_name nvarchar(15) = null, @sqlstat nvarchar(500) = null; set @col_name = 'Assoc_Item_' + Convert(nvarchar(2), @curr_row1); set @sqlstat = 'update ##RelPro set ' + @col_name + ' = (Select relsku From ##TSku Where tid = ' + Convert(nvarchar(2), @curr_row1) + ') Where RowID = ' + Convert(nvarchar(2), @curr_row); Exec(@sqlstat); set @curr_row1 = @curr_row1 + 1; End End set @curr_row = @curr_row + 1; End Select * From ##RelPro; 

END GO

我不认为这是可能的( 虽然参考下面的更新 ); 据我所知,一个表variables只存在于声明它的范围内。 但是,您可以使用临时表 (使用create table语法并在#表符号前加上表名),并且可以在创build它的范围内以及dynamic语句的范围内进行访问。

更新:请参阅Martin Smith的答案,了解如何使用表值参数将表variables传递给dynamicSQL语句。 还要注意提到的限制:表值参数是只读的。

使用临时表解决了这个问题,但我遇到了使用Exec的问题,所以我使用sp_executesql的下列解决scheme:

 Create TABLE #tempJoin ( Old_ID int, New_ID int); declare @table_name varchar(128); declare @strSQL nvarchar(3072); set @table_name = 'Object'; --build sql sting to execute set @strSQL='INSERT INTO '+@table_name+' SELECT '+@columns+' FROM #tempJoin CJ Inner Join '+@table_name+' sourceTbl On CJ.Old_ID = sourceTbl.Object_ID' **exec sp_executesql @strSQL;** 

以下是使用dynamicT-SQL查询的示例,如果您有多个返回值列(请注意dynamic表名称),请提取结果:

 DECLARE @strSQLMain nvarchar(1000), @recAPD_number_key char(10), @Census_sub_code varchar(1), @recAPD_field_name char(100), @recAPD_table_name char(100), @NUMBER_KEY varchar(10), if object_id('[Permits].[dbo].[myTempAPD_Txt]') is not null DROP TABLE [Permits].[dbo].[myTempAPD_Txt] CREATE TABLE [Permits].[dbo].[myTempAPD_Txt] ( [MyCol1] char(10) NULL, [MyCol2] char(1) NULL, ) -- an example of what @strSQLMain is : @strSQLMain = SELECT @recAPD_number_key = [NUMBER_KEY], @Census_sub_code=TEXT_029 FROM APD_TXT0 WHERE Number_Key = '01-7212' SET @strSQLMain = ('INSERT INTO myTempAPD_Txt SELECT [NUMBER_KEY], '+ rtrim(@recAPD_field_name) +' FROM '+ rtrim(@recAPD_table_name) + ' WHERE Number_Key = '''+ rtrim(@Number_Key) +'''') EXEC (@strSQLMain) SELECT @recAPD_number_key = MyCol1, @Census_sub_code = MyCol2 from [Permits].[dbo].[myTempAPD_Txt] DROP TABLE [Permits].[dbo].[myTempAPD_Txt]