web-dev-qa-db-ja.com

データを2つのテーブルに一度に分割する

SQL Server 2008を使用していますが、多くの一時テーブルと信頼性の低い結合を使用せずに解決する方法がわからないという問題があります。

表1には6列のデータが含まれており、2つのテーブルに分割されます。列1から列3は表2に、列4から列6は表3に、データを表2と表3に取り込むのは簡単です。ただし、表3のT2IDは、表2のIDへの外部キーです。

パフォーマンスが重要なので、変数を使用したり、データを行ごとに繰り返し処理したりせず、理想的には、1つの挿入で多くのことを実行したいだけです。

リンクテーブルを使用してみましたが、テーブル2とテーブル3のデータは一意ではないため、結合は信頼できません。

助言がありますか?

Create Table T1 (
  ID INT IDENTITY(1,1),
  Col1 VARCHAR(10),
  Col2 VARCHAR(10),
  Col3 VARCHAR(10),
  Col4 VARCHAR(10),
  Col5 VARCHAR(10),
  Col6 VARCHAR(10)
)

Create Table T2 (
  ID INT IDENTITY(1,1),
  Col1 VARCHAR(10),
  Col2 VARCHAR(10),
  Col3 VARCHAR(10)
)

Create Table T3 (
  ID INT IDENTITY(1,1),
  T2ID INT,
  Col4 VARCHAR(10),
  Col5 VARCHAR(10),
  Col6 VARCHAR(10)
)

T2またはT3を変更できません。 T1はステージングテーブルではなく、変更することもできません。 T1、T2、およびT3に書き込む他のパッケージもあります。必要に応じて、異なる時間にスケジュールを設定することも可能ですが。

5
user76664

MERGEを使用してT2にデータを挿入する場合、T1.IDT2.IDの間のマッピングテーブルを生成できます。

DECLARE @Mapping TABLE
(
  T1ID int,
  T2ID int
);
MERGE INTO
  dbo.T2 AS tgt
USING
  dbo.T1 AS src
ON
  1 = 0
WHEN NOT MATCHED THEN
  INSERT (    Col1,     Col2,     Col3)
  VALUES (src.Col1, src.Col2, src.Col3)
OUTPUT
  src.ID, inserted.ID INTO @Mapping (T1ID, T2ID)
;

OUTPUT句でのみinsertedテーブルの列を参照できるINSERTとは異なり、MERGEステートメントでは、ソーステーブルの列も参照できます。それがソースIDとターゲットIDを関連付ける方法なので、これがこのソリューションの鍵となります。

マッピングテーブルを作成したら、T3に挿入するときに結合で使用できます。今回は単純なINSERT ... SELECTが行います:

INSERT INTO
  dbo.T3 (T2ID, Col4, Col5, Col6)
SELECT
  m.T2ID,
  t.Col4,
  t.Col5,
  t.Col6
FROM
  dbo.T1 AS t
  INNER JOIN @Mapping AS m ON t.ID = m.T1ID
;

両方のステートメントを1つのトランザクションにラップして、分割操作をアトミックにします。

MERGEマッピング方法についての議論はこのスレッドにあります:

6
Andriy M

ここで半ば素晴らしいアイデア。このプロセスは機能するはずですが、ずさんな感じがします。

_DECLARE @t2CurrID int, @t1MinID int

BEGIN TRANSACTION

SELECT @t2CurrID = IDENT_CURRENT(T2)

SELECT @t1MinID = min(t1.ID) FROM T1 WHERE [Criteria used to decide what gets copied across]

INSERT INTO T2 (
    Col1, 
    Col2, 
    Col3
    )
SELECT 
    Col1,
    Col2,
    Col3
FROM T1
WHERE [Criteria used to decide what gets copied across]
ORDER BY T1.ID

INSERT INTO T3 (
    T2ID,
    Col4,
    Col5,
    Col6
    )
SELECT 
    Row_Number() Over (Order By T1.ID) + @t2CurrID as T2ID,
    Col4,
    Col5,
    Col6
FROM T1
WHERE [Criteria used to decide what gets copied across]
ORDER BY T1.ID

COMMIT TRANSACTION
_

プロセスのしくみ:IDENT_CURRENT(T2)コマンドは、T2に挿入された最後のID値を取得します。次に、T1.IDの順序でT2に挿入します。最後に、T2に挿入する前に、最後のIDから計算されたT2IDの値をT2に挿入し、データをT2に挿入した順序に基づいてRow_Number()を使用します。

したがって、最後のID値が100の場合、T2の最初の新しい行はID 101を取得し、T3の計算されたT2IDは(100 + 1)= 101になります。追加された100番目の行の場合、新しいT2.IDは次のようになります。 200、T3のT2IDは(100 + 100)= 200などになります。

最大の問題は、他のプロセスがテーブルに書き込んでいる場合の_IDENT_CURRENT_とトランザクションの間の相互作用です。ただし、ifトランザクションは正しい値を取得し、テーブルをロックします。期待どおりに、プロセスは正しいシーケンスを提供します。

トランザクションの期間中にテーブルをロックする必要がある場合は、いつでも発行できます。

_SELECT TOP 1 * from T2 WITH (TABLOCK)
_
2
Laughing Vergil

データを2つのテーブルに分割するときの私の経験では、SSISが最適です。説明させてください。 TSQLを使用してメインのインポートテーブルから2つのテーブルに移動することはできません。TSQLはトランザクションログに記録されるため、SSISよりも実行速度が遅くなります。 SSIS関数のみを使用する必要があり、データを2つの個別のテーブルに分割できる関数があります。 SSISがより高速に実行される理由は、sqlservr.exeではなくMsDtsSrvr.exeで実行されているためです。

0
Duane Lawrence