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.
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
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.
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;
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.
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
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]
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;**