How to use table variable in a dynamic sql statement?

178,540

Solution 1

Your EXEC executes in a different context, therefore it is not aware of any variables that have been declared in your original context. You should be able to use a temp table instead of a table variable as shown in the simple demo below.

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

Solution 2

On SQL Server 2008+ it is possible to use Table Valued Parameters to pass in a table variable to a dynamic SQL statement as long as you don't need to update the values in the table itself.

So from the code you posted you could use this approach for @TSku but not for @RelPro

Example syntax below.

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 

The physloc column is included just to demonstrate that the table variable referenced in the child scope is definitely the same one as the outer scope rather than a copy.

Solution 3

You don't have to use dynamic SQL

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;

Solution 4

You can't do this because the table variables are out of scope.

You would have to declare the table variable inside the dynamic SQL statement or create temporary tables.

I would suggest you read this excellent article on dynamic SQL.

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

Solution 5

Well, I figured out the way and thought to share with the people out there who might run into the same problem.

Let me start with the problem I had been facing,

I had been trying to execute a Dynamic Sql Statement that used two temporary tables I declared at the top of my stored procedure, but because that dynamic sql statment created a new scope, I couldn't use the temporary tables.

Solution:

I simply changed them to Global Temporary Variables and they worked.

Find my stored procedure underneath.

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

AS BEGIN -- SET NOCOUNT ON added to prevent extra result sets from -- interfering with SELECT statements. 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

Share:
178,540
Ashar Syed
Author by

Ashar Syed

Works as a Web Tech Programmer.

Updated on July 05, 2022

Comments

  • Ashar Syed
    Ashar Syed almost 2 years

    In my stored procedure I declared two table variables on top of my procedure. Now I am trying to use that table variable within a dynamic sql statement but I get this error at the time of execution of that procedure. I am using Sql Server 2008.

    This is how my query looks like,

    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);
    

    And I get the following errors,

    Must declare the table variable "@RelPro". Must declare the table variable "@TSku".

    I have tried to take the table outside of the string block of dynamic query but to no avail.

  • Dr. Wily's Apprentice
    Dr. Wily's Apprentice over 13 years
    I haven't tested your code, but I don't think your temp tables have to be global. You can probably use a non-global (local?) temp table (just use a single # symbol). The benefit is that it will automatically disappear once your connection is closed, and it is only visible within your connection. I.e. I believe that with a global temp table, if you have multiple connections executing the same procedure at the same time, they could clobber each other due to sharing the same global temp table.
  • ZygD
    ZygD over 13 years
    Sorry, you have a CURSOR too...?
  • Martin Smith
    Martin Smith over 13 years
    @Dr. Wily - Yep. I think global temp tables are needed if they are being created inside the dynamic SQL itself to stop them disappearing when the dynamic SQL batch finishes but if created outside the dynamic SQL batch local temp tables should be visible.
  • Dr. Wily's Apprentice
    Dr. Wily's Apprentice over 13 years
    @Martin - Ah, I just tried that, and it looks like you are correct; a temp table created inside of a dynamic statement disappears. However, that doesn't seem to be the scenario here. The temp tables are being created and then modified by dynamic statements.
  • John Spiegel
    John Spiegel over 9 years
    Perhaps I messed up, but doesn't the different context also cause the temp table to go out of scope unless using a global temp?
  • Adir D
    Adir D about 9 years
    @John No, the inner scope can see the #table created in the parent scope. Easy to test.
  • sheldonhull
    sheldonhull about 9 years
    I've understood that it passes the table by reference, so reducing the IO. Havent had a chance to verify this though in the case of a nested stored proc call or execute statement
  • FrenkyB
    FrenkyB over 7 years
    Perhaps some good link on sql server context? I really don't understand this. If dynamic sql and table variable are inside same batch - what role context plays here?