web-dev-qa-db-ja.com

変数を使用してSQL Server Mergeステートメントの列名を含める

マージジョブのために列リストの取得を自動化しようとしていて、ちょっとしたスクラッチツールに遭遇しています。すべての列名を手動で入力する必要はなく、変数を介してそれらを渡すことができると思いました。変数のコンテンツを選択すると、そのリストが正しく表示されます。更新コンポーネントが正しく記述されていないことはわかっていますが(それを効率化する方法について喜んでフィードバックします)、私が心配しているのは、「列名または指定された値の数がテーブル定義と一致しません。」

私が通過しているものは以下の通りです:

CREATE PROCEDURE sys_sp_merge_table_name

@targcolumns varchar(max),
@targtable varchar(50),
@sourcecolumns varchar(max)

AS
BEGIN
SET NOCOUNT ON;
set @targcolumns =  '' --column names for the table
set @targtable = 'targ_table_name' -- target table name
set @sourcecolumns = ''

--assigning column names to pass to variables using "Tsource" as alias for table name 
select @targcolumns = @targcolumns + column_name+',' from INFORMATION_SCHEMA.columns where table_name = @targtable
select @sourcecolumns = @sourcecolumns + 'Tsource.'+column_name+',' from INFORMATION_SCHEMA.columns where table_name = @targtable

--trim the trailing commas from target and source table
set @sourcecolumns = Left(@sourcecolumns,len(@sourcecolumns)-1)
set @targcolumns = Left(@targcolumns,len(@targcolumns)-1)

--run merge job, zdata is our linked server from which the data is imported
merge targ_table_name
using (select * from openquery(zdata,'select * from source_table_name'))as Tsource 
on (targ_table_name.ID = Tsource.ID) 
when matched then
Update set @targcolumns = @sourcecolumns
when not matched by target then
insert values (@sourcecolumns);
END
GO

編集:@Max Vernonセットピースの更新に関しては正しいです。これが作成されると作成されますが、205列に入力しなくても済むように、自動化したいと思います。さらに、これは他のテーブルに再適用されるので、それを行うための最良/最も効率的な方法を知りたいのです。パラメータを自動化して次のようにする方法:

Update set @targcolumns.A = @sourcecolumns.A, @[email protected], etc.

この特定のテーブルには205列ありますが、残念ながらテーブル構造は私たちが制御できるものではありません。全体として、約150のテーブルが定期的にマージされます。

Edit2:最終編集-将来これに遭遇した人のために私が使用しているソリューションを提供したかっただけです。これには、Maxが推奨する動的SQLと、SETステートメントを設定するためのクエリが含まれています

Declare
@targcolumns varchar(max),
@targtable varchar(50),
@sourcecolumns varchar(max),
@runmerge nvarchar(max),
@setstatement nvarchar(max)


SET NOCOUNT ON;
set @targcolumns =  '' --column names for the table
set @targtable = 'target_table_name' -- target table name
set @sourcecolumns = ''

--assigning column names to pass to variables using "Tsource" as alias for table name 
select @targcolumns = @targcolumns + column_name+',' from INFORMATION_SCHEMA.columns where table_name = @targtable
select @sourcecolumns = @sourcecolumns + 'Tsource.'+column_name+',' from INFORMATION_SCHEMA.columns where table_name = @targtable
select @setstatement = @setstatement + '[' + column_name + ']' + ' = ' + 'Tsource.'+'[' + column_name+']'+','from INFORMATION_SCHEMA.columns where table_name = @targtable

--trim the trailing commas from target and source table
set @sourcecolumns = Left(@sourcecolumns,len(@sourcecolumns)-1)
set @targcolumns = Left(@targcolumns,len(@targcolumns)-1)
set @setstatement = Left(@setstatement,len(@setstatement)-1)

--run merge job, zdata is our linked server from which the data is imported
set @runmerge=' merge target_table_name
using (select * from openquery(zdata,''select * from target_table_name''))as Tsource 
on (target_table_name.ID = Tsource.ID) 
when matched then
Update set ' + @setstatement + '
when not matched by target then
insert values ('+@sourcecolumns+');';

exec sp_executesql @runmerge;

この例ではすべての変数を使用しているわけではありませんが、すべてが必要な他の例があります。 Maxが私を正しい道に導いてくれたことを助けてくれてありがとう。

2
Josh K

私が使用しているソリューションを、将来この問題に遭遇した人に提供したかっただけです。これには、Maxが推奨する動的SQLと、SETステートメントを設定するためのクエリが含まれています。

Declare
@targcolumns varchar(max),
@targtable varchar(50),
@sourcecolumns varchar(max),
@runmerge nvarchar(max),
@setstatement nvarchar(max)


SET NOCOUNT ON;
set @targcolumns =  '' --column names for the table
set @targtable = 'target_table_name' -- target table name
set @sourcecolumns = ''

--assigning column names to pass to variables using "Tsource" as alias for table name 
select @targcolumns = @targcolumns + column_name+',' from INFORMATION_SCHEMA.columns where table_name = @targtable
select @sourcecolumns = @sourcecolumns + 'Tsource.'+column_name+',' from INFORMATION_SCHEMA.columns where table_name = @targtable
select @setstatement = @setstatement + '[' + column_name + ']' + ' = ' + 'Tsource.'+'[' + column_name+']'+','from INFORMATION_SCHEMA.columns where table_name = @targtable

--trim the trailing commas from target and source table
set @sourcecolumns = Left(@sourcecolumns,len(@sourcecolumns)-1)
set @targcolumns = Left(@targcolumns,len(@targcolumns)-1)
set @setstatement = Left(@setstatement,len(@setstatement)-1)

--run merge job, zdata is our linked server from which the data is imported
set @runmerge=' merge target_table_name
using (select * from openquery(zdata,''select * from target_table_name''))as Tsource 
on (target_table_name.ID = Tsource.ID) 
when matched then
Update set ' + @setstatement + '
when not matched by target then
insert values ('+@sourcecolumns+');';

exec sp_executesql @runmerge;

この例ではすべての変数を使用しているわけではありませんが、すべてが必要な他の例があります。 Maxが私を正しい道に導いてくれたことを助けてくれてありがとう。

1
Josh K

列名のリスト全体を入力する代わりに、SQL Server Management Studioのオブジェクトエクスプローラーウィンドウから列リストをドラッグアンドドロップするだけです。下の画像では、強調表示されたセクションをクエリウィンドウにドラッグアンドドロップします。

enter image description here

ただし、列のリストを自動的に作成する場合は、動的SQLを使用してMERGEコマンドを実行する必要があります。 Erland Sommarskogは、動的SQLの使用方法に関する決定的な記事と、notを行うためのすばらしいアドバイスのセットを http://www.sommarskog.se/dynamic_sql.html

このようなもの:

DECLARE @cmd NVARCHAR(MAX);
SET @cmd = 'merge targ_table_name
using (select * from openquery(zdata,''select * from source_table_name'')) 
     as Tsource 
on (targ_table_name.ID = Tsource.ID) 
when matched then
Update set ' + @TargetToSourceColumns + '
when not matched by target then
insert values (' + @SourceColumns + ');';

EXEC sp_executesql @cmd;

上記の文は構文的には正しくありませんが、クエリの動的な部分を連結する方法を本質的に示しています。必要に応じて、@TargetToSourceColumnsおよび@SourceColumns変数をコンパイルする必要があります。

3
Max Vernon