2011-01-07 12 views
50

В моей хранимой процедуре я описал две переменные таблицы поверх моей процедуры. Теперь я пытаюсь использовать эту переменную таблицы в динамическом выражении sql, но я получаю эту ошибку во время выполнения этой процедуры. Я использую SQL Server 2008.Как использовать переменную таблицы в динамической инструкции sql?

Это как мой запрос выглядит,

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

И я получаю следующие ошибки,

Необходимо объявить табличную переменную "@RelPro". Должен объявить переменную таблицы "@TSku".

Я попытался взять таблицу за строковый блок динамического запроса, но безрезультатно.

ответ

50

Ваш EXEC выполняет в другом контексте, поэтому он не знает каких-либо переменных, которые были объявлены в вашем исходном контексте. Вы должны иметь возможность использовать временную таблицу вместо переменной таблицы, как показано в простой демонстрации ниже.

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 
+1

Возможно, я испортил, но разве другой контекст также не приводит к тому, что таблица temp выходит за пределы области действия, если не используется глобальная температура? –

+1

@John Нет, внутренняя область видимости может видеть # таблицу, созданную в родительской области. Легко проверить. –

+0

Возможно, какая-то хорошая ссылка на контекст сервера sql? Я действительно не понимаю этого. Если динамическая sql и переменная таблицы находятся внутри одной партии - какой контекст роли здесь? – FrenkyB

6

Вы не можете этого сделать, потому что переменные таблицы выходят за рамки.

Вам нужно будет объявить переменную таблицы внутри динамического оператора SQL или создать временные таблицы.

Я бы предложил вам прочитать эту замечательную статью о динамическом SQL.

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

1

Я не думаю, что это возможно (хотя см обновление ниже); насколько я знаю, переменная таблицы существует только в пределах области, которая ее объявила. Тем не менее, вы можете использовать таблицу temp (используйте синтаксис create table и префикс своего имени таблицы с символом #), и это будет доступно как в области, создающей ее, так и в области вашей динамической инструкции.

ОБНОВЛЕНИЕ: см. Ответ Мартина Смита о том, как использовать параметр table-value, чтобы передать переменную таблицы в динамическую инструкцию SQL. Также обратите внимание на упомянутое ограничение: табличные параметры доступны только для чтения.

13

Вы не имеют использовать динамический 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; 
+1

+1 для нединамического sql-решения – JNK

1

Ну, я понял, как и думали, чтобы поделиться с людьми, которые там могли бы столкнуться с той же проблемой.

Позвольте мне начать с этой проблемой я столкнулась,

Я пытался выполнить заявление Dynamic Sql, который использовал два временных таблиц я объявил в верхней части моей хранимой процедуры, но и потому, что динамический SQL даного создал новую область, я не мог использовать временные таблицы.

Решение:

Я просто изменил их глобальные временные переменные, и они работали.

Найдите мою хранимую процедуру внизу.

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

AS НАЧАТЬ - SET NOCOUNT ON добавляется для предотвращения дополнительных наборов результатов из - мешая ЗЕЬЕСТ. 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

+4

Я не тестировал ваш код, но я не думаю, что ваши временные таблицы должны быть _global_. Вероятно, вы можете использовать таблицу неглобальных (локальных?) Temp (просто используйте один символ #). Преимущество в том, что он автоматически исчезнет после закрытия вашего соединения и будет виден только в вашем соединении. То есть Я считаю, что с глобальной временной таблицей, если у вас одновременно есть несколько соединений, выполняющих одну и ту же процедуру, они могут сбиваться друг с другом из-за обмена одной и той же глобальной временной таблицей. –

+4

Извините, у вас тоже есть КУРСОР ...? – gbn

+2

@Dr. Уайли - Да. Я думаю, что глобальные временные таблицы необходимы, если они создаются внутри самого динамического SQL, чтобы остановить их исчезновение, когда заканчивается динамическая партия SQL, но если они созданы за пределами динамических таблиц локальных временных таблиц SQL, должны быть видны. –

0

Использование таблицы Temp решает эту проблему, но я столкнулся с проблемами, с помощью Exec, так что я пошел со следующим раствором с помощью 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

Вот пример используя динамический запрос T-SQL, а затем извлечение результатов, если у вас есть более одного столбца возвращаемых значений (обратите внимание на имя динамической таблицы):

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] 
57

В SQL Server 2008+ можно использовать Табличные значения для передачи в переменной таблицы динамического оператора SQL, если вам не нужно обновлять значения в самой таблице.

Так из кода вы вывесили вы могли бы использовать этот подход для @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', 
    @[email protected] 

physloc колонка включается только, чтобы продемонстрировать, что переменная таблица ссылается на ребенка рамки, безусловно, такой же, как один внешней области, а не копии.

+0

Я не понимаю, почему это решение не было достаточно! Отличное и элегантное решение. –

+0

Я понял, что он передает таблицу по ссылке, поэтому уменьшает IO. У Havent была возможность проверить это, хотя в случае вложенного хранимого запроса proc или выполнения – SheldonH

 Смежные вопросы

  • Нет связанных вопросов^_^