web-dev-qa-db-ja.com

別のデータベースからのクエリの結果に基づいてデータベースをクエリする

VS 2013でSSISを使用しています。1つのデータベースからIDのリストを取得する必要があり、そのIDのリストを使用して、別のデータベース、つまりSELECT ... from MySecondDB WHERE ID IN ({list of IDs from MyFirstDB})にクエリを実行します。

9
faujong

これを実現する方法は3つあります。

1番目の方法-ルックアップ変換を使用する

まず、@ TheEsisiaが回答したように_Lookup Transformation_を追加する必要がありますが、さらに要件があります。

  • ルックアップで、IDリストを含むクエリを作成する必要があります(例:_SELECT ID From MyFirstDB WHERE ..._)

  • 少なくとも、ルックアップテーブルから1つの列を選択する必要があります

  • これらは行をフィルタリングしませんが、これにより2番目のテーブルから値が追加されます

行をフィルタリングするにはWHERE ID IN ({list of IDs from MyFirstDB})ルックアップエラー出力でいくつかの作業を行う必要があります_Error case_ 2つの方法があります。

enter image description here

  1. エラー処理を_Ignore Row_に設定して、追加された列(ルックアップから)の値がnullになるようにします。したがって、値がNULLに等しい行をフィルター処理する_Conditional split_を追加する必要があります。

ルックアップ列として_col1_を選択したと仮定すると、同様の式を使用する必要があります

_ISNULL([col1]) == False
_
  1. または、エラー処理を_Redirect Row_に設定して、すべての行がエラー出力行に送信され、使用されない可能性があるため、データがフィルタリングされます。

この方法の欠点は、実行中にすべてのデータがロードおよびフィルタリングされることです。

また、すべてのデータがロードされた後にローカルマシン(サーバー上の2番目の方法)でネットワークフィルタリングの作業が行われる場合は、メモリです。

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コマンド)

3番目の方法-SQL実行タスクの使用

2番目の方法と同様ですが、これは_Execute SQL Task_を使用してIN句を作成し、クエリ全体を_OLEDB Source_として使用します。

  1. DataFlowタスクの前にSQL実行タスクを追加するだけです
  2. ResultSetプロパティをsingleに設定します
  3. 結果セットとして_User::MyVariableList_を選択します
  4. 次の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
    _

enter image description here

enter image description here

_DataFlow Task_ _Delay Validation_プロパティがTrueに設定されていることを確認してください

11
Hadi

これは、LookUp Transformationを使用するための典型的なケースです。まず、OLE DB Sourceを使用して、最初のデータベースからデータを取得します。次に、LookUp Transformationを使用して、2番目のデータセットのID値に基づいてこのデータセットをフィルタリングします。 LookUp Transformationを使用する手順は次のとおりです。

  1. 次の図に示すように、[General]タブで、Full CashOLE DB Connection Manager、およびRedirect rows to no match outputを選択します。 Full Cashを使用すると、パッケージのパフォーマンスが向上することに注意してください。

一般設定

enter image description here

  1. Connectionタブで、OLE DB Connection Managerを使用して2番目のサーバーに接続します。次に、ID値を使用してデータセットを直接選択するか、(下の図に示すように)SQLコードを使用してフィルタリングデータセットからIDを選択できます。

接続:

enter image description here

  1. Columnsタブに移動し、両方のデータセットからID列を選択します。最初のデータセットの各レコードについて、そのIDAvailable LookUp Columnにあるかどうかを確認します。そうである場合は、Matching出力に移動し、そうでない場合はNo Matching出力に移動します。

一致ID列:

enter image description here

  1. OKをクリックして、LookUpを閉じます。次に、LookUp Match Outputを選択する必要があります。

一致出力:

enter image description here

5
TheEsnSiavashi

最初に文字列変数を作成します。パッケージのスコープでの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ステートメントソース。

1
Mike Honey

「最良の」答えは、関連するデータ量とソースシステムによって異なります。

他の回答の多くは、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はこれに基づいて異なる方法でソートされます)。

最後に、最後の段落を除いて、上記は整数があることを前提としています。文字列照合を実行している場合、さまざまなコンポーネントが大文字と小文字を区別する照合を実行する場合と実行しない場合があるため、醜さが増します(大文字と小文字を区別するシステムでの並べ替えも要因になる可能性があります)。

1
billinkc

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'
0
Julio Di Polito