VS 2013でSSISを使用しています。1つのデータベースからIDのリストを取得する必要があり、そのIDのリストを使用して、別のデータベース、つまりSELECT ... from MySecondDB WHERE ID IN ({list of IDs from MyFirstDB})
にクエリを実行します。
これを実現する方法は3つあります。
まず、@ TheEsisiaが回答したように_Lookup Transformation
_を追加する必要がありますが、さらに要件があります。
ルックアップで、IDリストを含むクエリを作成する必要があります(例:_SELECT ID From MyFirstDB WHERE ...
_)
少なくとも、ルックアップテーブルから1つの列を選択する必要があります
行をフィルタリングするにはWHERE ID IN ({list of IDs from MyFirstDB})
ルックアップエラー出力でいくつかの作業を行う必要があります_Error case
_ 2つの方法があります。
Ignore Row
_に設定して、追加された列(ルックアップから)の値がnullになるようにします。したがって、値がNULLに等しい行をフィルター処理する_Conditional split
_を追加する必要があります。ルックアップ列として_col1
_を選択したと仮定すると、同様の式を使用する必要があります
_ISNULL([col1]) == False
_
Redirect Row
_に設定して、すべての行がエラー出力行に送信され、使用されない可能性があるため、データがフィルタリングされます。この方法の欠点は、実行中にすべてのデータがロードおよびフィルタリングされることです。
また、すべてのデータがロードされた後にローカルマシン(サーバー上の2番目の方法)でネットワークフィルタリングの作業が行われる場合は、メモリです。
すべてのデータのロードを回避するために、回避策を実行できます。これは、スクリプトタスクを使用して実現できます:(VB.NETで記述された回答)
接続マネージャー名がTestAdo
であり、_"Select [ID] FROM dbo.MyTable"
_がIDのリストを取得するクエリであり、_User::MyVariableList
_が必要な変数であると仮定します。 IDのリストを保存します
注:このコードは、接続マネージャーから接続を読み取ります
_ Public Sub Main()
Dim lst As New Collections.Generic.List(Of String)
Dim myADONETConnection As SqlClient.SqlConnection
myADONETConnection = _
DirectCast(Dts.Connections("TestAdo").AcquireConnection(Dts.Transaction), _
SqlClient.SqlConnection)
If myADONETConnection.State = ConnectionState.Closed Then
myADONETConnection.Open()
End If
Dim myADONETCommand As New SqlClient.SqlCommand("Select [ID] FROM dbo.MyTable", myADONETConnection)
Dim dr As SqlClient.SqlDataReader
dr = myADONETCommand.ExecuteReader
While dr.Read
lst.Add(dr(0).ToString)
End While
Dts.Variables.Item("User::MyVariableList").Value = "SELECT ... FROM ... WHERE ID IN(" & String.Join(",", lst) & ")"
Dts.TaskResult = ScriptResults.Success
End Sub
_
そして、_User::MyVariableList
_をソースとして使用する必要があります(変数のSQLコマンド)
2番目の方法と同様ですが、これは_Execute SQL Task
_を使用してIN句を作成し、クエリ全体を_OLEDB Source
_として使用します。
ResultSet
プロパティをsingle
に設定しますUser::MyVariableList
_を選択します次のSQLコマンドを使用します
_DECLARE @str AS VARCHAR(4000)
SET @str = ''
SELECT @str = @str + CAST([ID] AS VARCHAR(255))
FROM dbo.MyTable
SET @str = 'SELECT * FROM MySecondDB WHERE ID IN (' + SUBSTRING(@str,1,LEN(@str) - 1) + ')'
SELECT @str
_
_DataFlow Task
_ _Delay Validation
_プロパティがTrue
に設定されていることを確認してください
これは、LookUp Transformation
を使用するための典型的なケースです。まず、OLE DB Source
を使用して、最初のデータベースからデータを取得します。次に、LookUp Transformation
を使用して、2番目のデータセットのID
値に基づいてこのデータセットをフィルタリングします。 LookUp Transformation
を使用する手順は次のとおりです。
General
]タブで、Full Cash
、OLE DB Connection Manager
、およびRedirect rows to no match output
を選択します。 Full Cash
を使用すると、パッケージのパフォーマンスが向上することに注意してください。一般設定
Connection
タブで、OLE DB Connection Manager
を使用して2番目のサーバーに接続します。次に、ID
値を使用してデータセットを直接選択するか、(下の図に示すように)SQLコードを使用してフィルタリングデータセットからIDを選択できます。接続:
Columns
タブに移動し、両方のデータセットからID
列を選択します。最初のデータセットの各レコードについて、そのID
がAvailable LookUp Column
にあるかどうかを確認します。そうである場合は、Matching
出力に移動し、そうでない場合はNo Matching
出力に移動します。一致ID列:
OK
をクリックして、LookUp
を閉じます。次に、LookUp Match Output
を選択する必要があります。一致出力:
最初に文字列変数を作成します。パッケージのスコープでのSQL_Select。次に、最初のデータベースに対してSQL実行タスクを使用してその値を割り当てます。 GeneralページのResultSetプロパティは単一行に設定する必要があります。 結果セットタブにエントリを追加して、変数に割り当てます。
使用するSQLステートメントは、2番目のデータベースに必要なSELECTステートメントを1行のテキストで返すように設計する必要があります。例を以下に示します。
SELECT
'SELECT * from MySecondDB WHERE ID IN ( '
+ STUFF ( (
SELECT TOP 5
' , ''' + [name] + ''''
FROM dbo.spt_values
FOR XML PATH(''), TYPE).value('(./text())[1]', 'VARCHAR(4000)'
) , 1 , 3, '' )
+ ' ) '
AS SQL_Select
TOP 5を削除し、[name]およびdbo.spt_valuesを列名とテーブル名に置き換えます。
次に、ダウンストリームタスクで変数SQL_Selectを使用できます。 an OLEデータベース2に対するDBソース。OLE DBソースとOLE DBコマンドタスクの両方で、変数を次のように指定できます。 SQLステートメントソース。
「最良の」答えは、関連するデータ量とソースシステムによって異なります。
他の回答の多くは、SQLServer内の巧妙な連結に基づいて値のリストを作成することを提案しています。参照されるシステムがOracle、MySQL、DB2、Informix、PostGresなどの場合、これはあまりうまく機能しません。mayと同等の概念がありますが、 notではないかもしれません。
最高のパフォーマンスを得るには、これらの行のいずれかがデータフローにヒットする前に、2番目のデータベースに対してフィルタリングする必要があります。これは、他の人が示唆しているように、ソースクエリにフィルタリング条件を追加することを意味します。このアプローチの課題は、クエリが私が覚えていないいくつかの実用的な範囲によって制限されることです。 where句の10、100、1000の値はおそらく問題ありません。百万ルピー、おそらくそれほど多くはありません。
ソーステーブルに対してフィルタリングする値が大量にある場合は、そのサーバー上にテーブルを作成し、そのテーブルを切り捨てて再読み込みする(SQLタスク+データフローを実行する)のが理にかなっています。これにより、すべてのデータをローカルにすることができ、フィルターテーブルにインデックスを付けて、データベースエンジンに実際に得意なことを実行させることができます。
ただし、ソースデータベースはテーブルを作成できないカスタムソリューションであると言います。一時テーブルを使用して上記のアプローチを確認できます。SSIS内では、接続をシングルトン/永続としてマークする必要があります(TODO:これを調べてください) )。 SSISを使用した一時テーブルのデバッグは、致命的な敵には望まない悪夢であるため、あまり気にしません。
まだ読んでいる場合は、最高のパフォーマンスが得られるとしても、ソースシステムでのフィルタリングが「実行可能」でない理由を特定しました。
今、私たちは純粋にSSISソリューションで立ち往生しています。最高のパフォーマンスを得るには、すべての列が絶対に必要な場合を除いて、ドロップダウンでテーブル名を選択しないでください。また、データ型にも注意してください。 LOB(XML、テキスト、イメージ(n)varchar(max)、varbinary(max))をデータフローに取り込むことは、パフォーマンスが低下するレシピです。
デフォルトの提案は、ルックアップコンポーネントを使用してデータフロー内のデータをフィルタリングすることです。ソースシステムがサポートし、OLE DBプロバイダーである限り(またはデータを強制的に キャッシュ接続マネージャー )
何らかの理由でルックアップコンポーネントを使用できない場合は、ソースシステムでデータを明示的に並べ替え、ソースコンポーネントにそのようにマークを付けてから、データフローで内部結合タイプのマージ結合を使用してのみ取り込むことができます。一致したデータ。
ただし、ソースシステムでの並べ替えは、ネイティブルールに従って並べ替えられることに注意してください。 SQLServerがデフォルトのASCIIソートに基づいてソートし、zOSで実行されているDB2インスタンスがEBCDICソートを提供するという状況に遭遇しました。これは、ドメインが整数のみである場合に最適でした。キーが英数字になったときにハンドバスケットで地獄に落ちます(AAA、A2B、およびAZZはこれに基づいて異なる方法でソートされます)。
最後に、最後の段落を除いて、上記は整数があることを前提としています。文字列照合を実行している場合、さまざまなコンポーネントが大文字と小文字を区別する照合を実行する場合と実行しない場合があるため、醜さが増します(大文字と小文字を区別するシステムでの並べ替えも要因になる可能性があります)。
2つのサーバーの間にリンクサーバーを追加できます。 SQLコマンドは次のようになります。
EXEC sp_addlinkedserver @server='SRV' --or any name you want
EXEC sp_addlinkedsrvlogin 'SRV', 'false', null, 'username', 'password'
SELECT * FROM SRV.CatalogNameInSecondDB.dbo.SecondDBTableName s
INNER JOIN FirstDBTableName f on s.ID = f.ID
WHERE f.ID IN (list of values)
EXEC sp_dropserver 'SRV', 'droplogins'