0

私は最近、sp_MSforeachtable ストアド プロシージャを使用して、テーブル名に Transcode という単語が含まれるすべてのテーブルを選択し、それらのテーブルで SQL を実行したいという問題を発見しました。正常にスキップされることを望んでいたテーブル (つまり、名前にトランスコードが含まれていないテーブル) については、機能するコードを書くことができましたが、完全ではありませんでした。トランスコード テーブルに存在する) それらのテーブルには存在しません。問題は、必要な場合 (条件が満たされた場合など) にのみ SQL を解析するのではなく、ストアド プロシージャが呼び出されたときにすべての SQL が解析されることです。

次のコードは期待どおりに機能します。

exec sp_MSforeachtable '
print ''Table being tested: ?''
if exists (select 1 where ''?'' like ''%Transcode%'')
begin
    print ''    Do Something''
end
else
begin
    print ''    Ignored''
end
'

ただし、機能を追加しようとすると、実行されないコードからエラーが発生します。例えば

exec sp_MSforeachtable '
print ''Table being tested: ?''
if exists (select 1 where ''?'' like ''%Transcode%'')
begin
    print ''    Do Something''
    
    insert ? (col1, col2, col3)
    select col1, col2, 1
    from ?
    where col3 = 0
    
end
else
begin
    print ''    Ignored''
end
'

今回は、テーブル名に Transcode という単語が含まれている場合は最初の出力と同じ出力が得られますが、含まれていない場合は Ignored と表示されるのではなく、次のように表示されます。

メッセージ 207、レベル 16、状態 1、行 9

無効な列名 col3

これは、動的 SQL が解析される方法にかかっていると確信していますが、望ましくない動作です。誰もこれに遭遇したことがありますか/簡単な回避策はありますか?

私の場合、存在しない列のおかげでエラーはとにかくifステートメントと同じ効果があり、有効な行は正常に実行できたので、これは緊急ではありませんが、何かをする必要がある場合に備えて学びたいと思っていますこの動作が問題を引き起こす場合も同様です。

前もって感謝します、

JB

ps。この動作を再現するコードを以下に示します。

create table DemoTranscode1 (id bigint identity(1,1) primary key clustered, col1 nvarchar(10) not null, col2 nvarchar(10)not null, col3 bit not null)
go
create table DemoTable1 (id bigint identity(1,1) primary key clustered, col1 nvarchar(10) not null, col2 nvarchar(10)not null)
go
create table DemoTranscode2 (id bigint identity(1,1) primary key clustered, col1 nvarchar(10) not null, col2 nvarchar(10)not null, col3 bit not null)
go
create table DemoTranscode3 (id bigint identity(1,1) primary key clustered, col1 nvarchar(10) not null, col2 nvarchar(10)not null, col3 bit not null)
go
insert DemoTranscode1
select 'example1', 'demo', 0
union select 'example2', 'demo', 0
union select 'example3', 'demo', 0
union select 'example4', 'demo', 0
insert DemoTable1 select col1, col2 from DemoTranscode1
insert DemoTranscode2 select col1, col2, col3 from DemoTranscode1
insert DemoTranscode3 select col1, col2, col3 from DemoTranscode1
4

3 に答える 3

3

1 つには、ドキュメント化されておらず、サポートされていない手順には近づかないことをお勧めしますsp_MSForEachTable。それらはいつでも SQL Server から変更または削除することができます。この特定の手順では、sp_MSForEachDb. (ここここでいくつかの背景を参照してください。)

これが私がそれを行う方法です:

DECLARE @sql NVARCHAR(MAX);
SELECT @sql = N'';

SELECT @sql = @sql + 'INSERT ' 
  + QUOTENAME(SCHEMA_NAME([schema_id]))
  + '.' + QUOTENAME(name) + ' (col1, col2, col3)
  SELECT col1, col2, 1 FROM '
  + QUOTENAME(SCHEMA_NAME([schema_id]))
  + '.' + QUOTENAME(name)
  + ' WHERE col3 = 0;'
FROM sys.tables 
WHERE name LIKE '%Transcode%';

PRINT @sql;
-- EXEC sp_executesql @sql;

これの良いところは、実行前に出力を簡単に検証できることです。

于 2012-04-23T13:52:48.520 に答える
0

問題はパーサーにあります。何らかの条件で sp_msforeachtable を使用するかどうかにかかわらず、各テーブルを解析します。したがって、他のテーブルの場合、エラーがスローされます。以下に示すように、execステートメントを使用して回避できます-

exec sp_MSforeachtable ' 
print ''Table being tested: ?'' 
if exists (select 1 where ''?'' like ''%Transcode%'') 
begin 
    print ''    Do Something'' 

    exec ( ''insert ? (col1, col2, col3) select col1, col2, 1 from ? where col3 = 0 '') 

end 
else 
begin 
    print ''    Ignored'' 
end 
' 
于 2012-04-23T14:03:41.337 に答える
0

パラメータを使用できるため、@whereandコードをチェックインする必要はありません。

exec sp_MSforeachtable
  @Command1 = 'print "?"',
  @whereand = ' and o.name like ''%Transcode%'''

アップデート:

これは動的 SQL が解析される方法にかかっていると確信していますが、これは望ましくない動作です。誰もこれに遭遇したことがありますか

もちろん。コードは実行前にコンパイルされ、コンパイラはテーブルに対して挿入ステートメントで使用される列名をチェックします。

于 2012-04-23T13:52:34.827 に答える