6

アプリケーションがテーブル内の ID を正しく更新する必要があるときに正しく更新しないという問題に対処するために、既存のクライアント データを構成する必要がある状況があります。

これがシナリオです。既存の行を効果的に置き換える行を挿入できる親テーブルがあります。置換は再帰的に行うことができます。親テーブルを指すフィールドを持つ子テーブルもあります。既存のデータでは、子テーブルが置換された行を指している可能性があり、それを修正する必要があります。ただし、各行を置換行に単純に更新することはできません。その行も置換されている可能性があり、最新の行を反映する必要があるためです。

これを実現する CTE を作成する方法を見つけようとしていましたが、実際に探しているものを見つけるクエリを見つけるのに苦労しています。これは、私が使用しているテーブルのサンプルです。'ShouldBe' 列は、いくつかの行の再帰的な置換を考慮して、更新クエリを終了させたいものです。

DECLARE @parent TABLE (SampleID int, 
                   SampleIDReplace int,
                   GroupID char(1))

INSERT INTO @parent (SampleID, SampleIDReplace, GroupID)
VALUES (1, -1, 'A'), (2, 1, 'A'), (3, -1, 'A'), 
       (4, -1, 'A'), (5, 4, 'A'), (6, 5, 'A'),
       (7, -1, 'B'), (8, 7, 'B'), (9, 8, 'B')


DECLARE @child TABLE (ChildID int, ParentID int)
INSERT INTO @child (ChildID, ParentID)
VALUES (1, 4), (2, 7), (3, 1), (4, 3)

更新スクリプトが適用された後、子テーブルで望ましい結果が得られます。

ChildID     ParentID    ParentID_ShouldBe
1           4           6 (4 replaced by 5, 5 replaced by 6)
2           7           9 (7 replaced by 8, 8 replaced by 9)
3           1           2 (1 replaced by 2)
4           3           3 (unchanged, never replaced)
4

3 に答える 3

4

以下は、探しているものを返します。

with cte as (
    select sampleid, sampleidreplace, 1 as num
    from @parent
    where sampleidreplace <> -1
    union all
    select p.sampleid, cte.sampleidreplace, cte.num+1
    from @parent p join
         cte
         on p.sampleidreplace = cte.sampleId
)
select c.*, coalesce(p.sampleid, c.parentid)
from @child c left outer join
     (select ROW_NUMBER() over (partition by sampleidreplace order by num desc) as seqnum, *
      from cte
     ) p
     on c.ParentID = p.SampleIDReplace and p.seqnum = 1

再帰部分は、すべての対応を追跡します (4-->5、4-->6)。加算数は「世代」カウントである。私たちは実際に最後の世代を望んでいます。これはrow_number()関数を使用して識別され、num の降順で並べ替えられます。したがって、p.seqnum = 1.

于 2012-12-26T15:08:11.723 に答える
2

わかりましたので、しばらく時間がかかりました。おそらくもっと良い方法がありますが、ここに1つのオプションがあります。

DECLARE @parent TABLE (SampleID int, 
                   SampleIDReplace int,
                   GroupID char(1))

INSERT INTO @parent (SampleID, SampleIDReplace, GroupID)
VALUES (1, -1, 'A'), (2, 1, 'A'), (3, -1, 'A'), 
       (4, -1, 'A'), (5, 4, 'A'), (6, 5, 'A'),
       (7, -1, 'B'), (8, 7, 'B'), (9, 8, 'B')


DECLARE @child TABLE (ChildID int, ParentID int)
INSERT INTO @child (ChildID, ParentID)
VALUES (1, 4), (2, 7), (3, 1), (4, 3)


;WITH RecursiveParent1 AS
(
    SELECT SampleIDReplace, SampleID, 1 RecursionLevel
    FROM @parent
    WHERE SampleIDReplace != -1
    UNION ALL
    SELECT A.SampleIDReplace, B.SampleID, RecursionLevel + 1
    FROM RecursiveParent1 A
    INNER JOIN @parent B
        ON A.SampleId = B.SampleIDReplace
),RecursiveParent2 AS
(
    SELECT  *, 
            ROW_NUMBER() OVER(PARTITION BY SampleIdReplace ORDER BY RecursionLevel DESC) RN
    FROM RecursiveParent1
)
SELECT A.ChildID, ISNULL(B.ParentID,A.ParentID) ParentID
FROM @child A
LEFT JOIN ( SELECT SampleIDReplace, SampleID ParentID 
            FROM RecursiveParent2
            WHERE RN = 1) B
    ON A.ParentID = B.SampleIDReplace
OPTION(MAXRECURSION 500)
于 2012-12-26T15:10:10.300 に答える
0

これを次のように整理すると思われる反復SQLループがあります。

WHILE EXISTS (SELECT * FROM #child C INNER JOIN #parent P ON C.ParentID = P.SampleIDReplace WHERE P.SampleIDReplace > -1)
BEGIN
    UPDATE #child
    SET ParentID = SampleID
    FROM #parent 
    WHERE #child.ParentID = SampleIDReplace
END

基本的に、while 条件は、子テーブルの親 ID 列の内容を比較し、親テーブルの SampleIDReplace 列に一致する値があるかどうかを確認します。存在する場合は、そのレコードの SampleID を取得します。結合の結果、すべての SampleIDReplace が -1 になったときにのみ停止します。つまり、他に何もする必要がありません。

サンプル データでは、上記の結果が期待される出力になります。

ループ内でテーブルにアクセスできるようにするために、ここではテーブル変数ではなく一時テーブルを使用する必要があったことに注意してください。テーブル変数を使用する必要がある場合は、もう少し手術が必要になります。

明らかに、置換階層が深い場合は、かなりの数の更新を行うことになります。これは、運用データベースに対してクエリを実行する際の考慮事項になる可能性があります。

于 2012-12-26T15:08:49.010 に答える