risposta-alla-domanda-sullo-sviluppo-web-bd.com

Come utilizzare la variabile di tabella in un'istruzione SQL dinamica?

Nella mia stored procedure ho dichiarato due variabili di tabella in cima alla mia procedura. Ora sto cercando di usare quella variabile di tabella all'interno di un'istruzione SQL dinamica ma ottengo questo errore al momento dell'esecuzione di quella procedura. Sto usando SQL Server 2008.

Ecco come appare la mia query, 

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

E ho i seguenti errori, 

Deve dichiarare la variabile di tabella "@RelPro" . Deve dichiarare la variabile di tabella "@TSku".

Ho provato a prendere la tabella al di fuori del blocco di stringhe della query dinamica ma inutilmente.

57
Ashar Syed

L'EXEC viene eseguito in un contesto diverso, pertanto non è a conoscenza di alcuna variabile dichiarata nel contesto originale. Dovresti essere in grado di utilizzare una tabella temporanea invece di una variabile di tabella, come mostrato nella semplice demo di seguito.

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
58
Joe Stefanelli

Su SQL Server 2008+ è possibile utilizzare i parametri con valori di tabella per passare in una variabile di tabella a un'istruzione SQL dinamica purché non sia necessario aggiornare i valori nella tabella stessa.

Quindi dal codice che hai postato potresti utilizzare questo approccio per @TSku ma non per @RelPro

Esempio di sintassi di seguito.

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',
  @[email protected] 

La colonna physloc è inclusa solo per dimostrare che la variabile di tabella a cui fa riferimento l'ambito figlio è sicuramente la stessa dell'ambito esterno anziché una copia.

74
Martin Smith

Non si ha per usare l'SQL dinamico

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;
13
gbn

Non è possibile farlo perché le variabili di tabella non sono comprese nell'ambito. 

Dovresti dichiarare la variabile table all'interno dell'istruzione SQL dinamica o creare tabelle temporanee.

Ti suggerirei di leggere questo eccellente articolo su SQL dinamico.

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

6
codingbadger

Bene, ho capito la strada e ho pensato di condividere con le persone là fuori che potrebbero incontrare lo stesso problema.

Permettetemi di iniziare con il problema che ho dovuto affrontare

Stavo cercando di eseguire un Dynamic Sql Statement che utilizzava due tabelle temporanee che ho dichiarato all'inizio della mia stored procedure, ma poiché quella dinamica sql statment ha creato un nuovo scope, non ho potuto usare le tabelle temporanee.

Soluzione:

Li ho semplicemente modificati in Global Temporary Variables e hanno funzionato.

Trova la mia stored procedure sotto.

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

AS BEGIN - SET NOCOUNT ON aggiunto per evitare set di risultati aggiuntivi da - interferire con le istruzioni 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;

FINE GO

2
Ashar Syed

Non penso che sia possibile ( sebbene si riferisca all'aggiornamento sotto ); per quanto ne so una variabile table esiste solo nell'ambito che lo ha dichiarato. Tuttavia, puoi usare una temp table (usa la sintassi create table e prefissa il nome della tabella con il simbolo #), e ciò sarà accessibile sia nell'ambito che lo crea sia nell'ambito della tua dichiarazione dinamica.

AGGIORNAMENTO: fare riferimento alla risposta di Martin Smith su come utilizzare un parametro con valori di tabella per passare una variabile di tabella in un'istruzione SQL dinamica. Notare anche la limitazione menzionata: i parametri con valori di tabella sono di sola lettura.

Ecco un esempio dell'utilizzo di una query T-SQL dinamica e quindi dell'estrazione dei risultati se si dispone di più di una colonna di valori restituiti (notare il nome della tabella dinamica):

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]  
0
Gerald

L'utilizzo della tabella Temp risolve il problema, ma mi sono imbattuto in problemi con Exec, quindi sono andato con la seguente soluzione di utilizzo di sp_executesql:

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 '[email protected]_name+' SELECT '[email protected]+' FROM #tempJoin CJ
                        Inner Join '[email protected]_name+' sourceTbl On CJ.Old_ID = sourceTbl.Object_ID'

**exec sp_executesql @strSQL;**
0
sfomate