73

ストアド プロシージャでは、プロシージャの上に 2 つのテーブル変数を宣言しました。現在、動的SQLステートメント内でそのテーブル変数を使用しようとしていますが、そのプロシージャの実行時にこのエラーが発生します。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);

そして、次のエラーが表示されます。

テーブル変数「@RelPro」を宣言する必要があります。テーブル変数「@TSku」を宣言する必要があります。

動的クエリの文字列ブロックの外にあるテーブルを取得しようとしましたが、役に立ちませんでした。

4

8 に答える 8

85

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',
  @T=@T 

このphysloc列は、子スコープで参照されるテーブル変数がコピーではなく、外側のスコープと確実に同じであることを示すために含まれています。

于 2012-10-13T20:38:08.570 に答える
74

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
于 2011-01-07T14:19:55.003 に答える
14

動的 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;
于 2011-01-07T14:26:58.080 に答える
6

テーブル変数がスコープ外であるため、これを行うことはできません。

動的 SQL ステートメント内でテーブル変数を宣言するか、一時テーブルを作成する必要があります。

動的 SQL に関するこの優れた記事を読むことをお勧めします。

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

于 2011-01-07T14:18:07.283 に答える
2

さて、私は方法を考え出し、同じ問題に遭遇する可能性のある人々と共有することを考えました.

私が直面していた問題から始めましょう。

ストアド プロシージャの先頭で宣言した 2 つの一時テーブルを使用する動的 SQL ステートメントを実行しようとしましたが、その動的 SQL ステートメントが新しいスコープを作成したため、一時テーブルを使用できませんでした。

解決:

それらをグローバル一時変数に変更しただけで機能しました。

下にあるストアド プロシージャを見つけます。

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

AS BEGIN -- 余分な結果セットを防ぐために追加された SET NOCOUNT ON -- SELECT ステートメントに干渉します。NOCOUNT をオンに設定します。

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;

エンドゴー

于 2011-01-07T14:44:03.880 に答える
2

私はそれが可能だとは思いません (ただし、以下の更新を参照してください)。私の知る限り、テーブル変数はそれを宣言したスコープ内にのみ存在します。ただし、一時テーブルcreate tableを使用して (構文を使用し、テーブル名の前に # 記号を付けます)、一時テーブルを作成するスコープと動的ステートメントのスコープの両方でアクセスできます。

更新: テーブル値パラメーターを使用してテーブル変数を動的 SQL ステートメントに渡す方法については、Martin Smith の回答を参照してください。前述の制限にも注意してください。テーブル値パラメーターは読み取り専用です。

于 2011-01-07T14:18:51.280 に答える
1

以下は、動的 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]  
于 2012-07-24T15:30:40.660 に答える
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 '+@table_name+' SELECT '+@columns+' FROM #tempJoin CJ
                        Inner Join '+@table_name+' sourceTbl On CJ.Old_ID = sourceTbl.Object_ID'

**exec sp_executesql @strSQL;**
于 2011-04-13T23:02:56.727 に答える