web-dev-qa-db-ja.com

自己参照CTE SQLサーバーに関するヘルプ

以下の表に、レベルの数が異なる階層があります。この例では、7つのレベルのサブセットを選択しました。

L_Key   Parent  Level P_Key
-------------------------
1393    NULL    0     2
1399    1393    1     325
4485    1399    2     NULL
4505    4485    3     NULL
5066    4505    4     NULL
6121    5066    5     NULL
5068    6121    6     NULL

私の目標は、NULLでない親のプロパティを取得することです。したがって、この場合、これは返されるはずです。

L_Key   Parent  Level P_Key
-------------------------
1393    NULL    0     2
1399    1393    1     325
4485    1399    2     325
4505    4485    3     325
5066    4505    4     325
6121    5066    5     325
5068    6121    6     325

これは私がこれまでに試したことです。

;WITH CTE AS (
    SELECT 
        m.Functional_Location_Key           
        ,m.Parent_Key
        ,m.Level
        ,m.Property_Key
    FROM #MappingPartOne m
    WHERE m.Functional_Location_Key IN (6121,5068,5066,4505,4485,1399,1393)

    UNION ALL 

    SELECT 
        t.Functional_Location_Key           
        ,t.Parent_Key
        ,t.Level
        ,t.Property_Key
    FROM CTE t
    JOIN #MappingPartOne b ON b.Parent_Key = t.Functional_Location_Key
)
SELECT  
    Functional_Location_Key         
    ,Parent_Key
    ,Level
    ,Property_Key
FROM CTE
option (maxrecursion 0)

これを除いて、無限ループが発生します。

ご協力ありがとうございました。

テスト用にテーブル変数を設定しました

DECLARE @MappingPartOne TABLE (
    Functional_Location_Key int,
    Parent_Key int,
    Level int,
    Property_Key int
)
INSERT INTO @MappingPartOne VALUES (1393,   NULL,   0,  2)
INSERT INTO @MappingPartOne VALUES (1399,   1393,   1,  325)
INSERT INTO @MappingPartOne VALUES (4485,   1399,   2,  NULL)
INSERT INTO @MappingPartOne VALUES (4505,   4485,   3,  NULL)
INSERT INTO @MappingPartOne VALUES (5066,   4505,   4,  NULL)
INSERT INTO @MappingPartOne VALUES (6121,   5066,   5,  NULL)
INSERT INTO @MappingPartOne VALUES (5068,   6121,   6,  NULL)
3
Kevin Nguyen

追加できるレコードが常に見つかるので、クエリは無限ループを実行しています。これは、クエリの後半(UNION ALLの後)に追加する行が、クエリの前半にある正確な行であるためです。 SELECTリストでCTEではなく_@MappingPartOne_テーブルの列を使用する場合、無限ループは発生しません。 wantは得られませんが、無限ループは得られません。

私が正しく理解している場合、行の_Property_Key_がNULLの場合、行の親で使用されているのと同じ_Property_Key_値を使用する必要があります。つまり、最も近い祖先の_Property_Key_、その値はNULLではありません。例:_Functional_Location_Key_ = 4505の行にNULLではなく_Property_Key_ = 9978が含まれる場合、その子孫(_Functional_Location_Key_ 5066、6121、および5068)は_Property_Key_ = 9978を表示します。

これを行うには、クエリにいくつかの変更を加える必要があります。

  • まず、前述のように、CTEの2番目の部分は、CTEではなく_@MappingPartOne_テーブルから列を出力する必要があります。ただし、これにより、行の複数のコピーが得られます。

  • 各行の単一のコピーを取得するには、CTEを異なる方法で開始する必要があります。最初は、CTEの最初の部分に表示する行のリストを明示的に提供しています。ただし、2番目の部分では、前の反復からの各行の子孫を取り込みます。最初の部分に、含めた他の行の子孫である行を含めたので、行を反復するときに重複が発生します。

    代わりに、最終的な親行(他の行の子孫ではない行)のみを指定する必要があります。つまり、_Parent_Key_がNULLである行(この場合は行)です。

  • 次のステップは、行の_Property_Key_がNULLかどうかを確認し、(そうであれば)親と同じ値を使用することです。 its親の_Property_Key_を使用するため、親の_Property_Key_もNULLであるかどうかは関係ありません。データを反復するたびに、現在の行(_@MappingPartOne_から)とその親(CTEから)が得られます。したがって、単に_@MappingPartOne.Property_Key_を使用する代わりに、それがNULLかどうかを確認します。そうでない場合は、それを使用します。そうである場合は、_CTE.Property_Key_を使用します。

したがって、これにより次のクエリが得られます。

_WITH CTE AS (
    SELECT 
        m.Functional_Location_Key           
        ,m.Parent_Key
        ,m.Level
        ,m.Property_Key
    FROM #MappingPartOne m
    WHERE m.Parent_Key IS NULL

    UNION ALL 

    SELECT 
        b.Functional_Location_Key           
        ,b.Parent_Key
        ,b.Level
        ,COALESCE(b.Property_Key, t.Property_Key) as Property_Key
    FROM CTE t
    JOIN #MappingPartOne b ON b.Parent_Key = t.Functional_Location_Key
)
SELECT  
    Functional_Location_Key         
    ,Parent_Key
    ,Level
    ,Property_Key
FROM CTE
;
_

この動作は this SQLFiddle link で確認できます。

(これまでに使用したことがない場合、COALESCE()ISNULL()関数のように機能します。2つの潜在的な利点があります。3つ以上の引数を指定できるため、この場合、最初の非NULL引数、またはすべてがNULLの場合はNULL、それはANSI標準の一部であるため、他のSQL方言に移植できる可能性が高くなります。

CTEの最初の部分でWHERE句の条件を変更して、特定の行を_Functional_Location_Key_で選択する場合、残りの階層を無視して、任意の行とその子孫をプルアップできます。必要な場合に備えて。


コメントごとに、親を持つ最高レベルの祖先からの_Property_Key_(roe 1393からではなく、行1399から)をNULLを持つすべての子孫のデフォルトとして表示するように見えます_Property_Key_ 。

これを行うには、CTEに列を追加するだけで済みます。

_WITH CTE AS (
    SELECT 
        m.Functional_Location_Key           
        ,m.Parent_Key
        ,m.Level
        ,m.Property_Key
        ,NULL as Top_Property_Key
    FROM MappingPartOne m
    WHERE m.Parent_Key IS NULL

    UNION ALL 

    SELECT 
        b.Functional_Location_Key           
        ,b.Parent_Key
        ,b.Level
        ,COALESCE(b.Property_Key, t.Top_Property_Key) as Property_Key
        ,COALESCE(t.Top_Property_Key, b.Property_Key) as Top_Property_Key
    FROM CTE t
    JOIN MappingPartOne b ON b.Parent_Key = t.Functional_Location_Key
)
SELECT  
    Functional_Location_Key         
    ,Parent_Key
    ,Level
    ,Property_Key
FROM CTE
;
_

新しい列は_Top_Property_Key_です。最初は、最上位の親を見ています。これをデフォルトとして使用したくないので、この1つの行の値をNULLに設定しました。 CTEの2番目の部分では、確立された_Top_Property_Key_が現在NULLでない場合はデフォルトとして使用し、それ以外の場合は現在の親行の_Property_Key_を使用します。 _Top_Property_Key_にNULL以外の値が割り当てられている行があると、そのすべての子の値が保持されます。

最後に、各行の_Property_Key_のCOALESCEを変更して、_t.Top_Property_Key_(直接の親のプロパティキー値)ではなく、新しい列_t.Property_Key_を使用します。

これは、新しい機能を備えた更新済みの SQLFiddle link です。

注:最上位の親の_Property_Key_をそのすべての子のデフォルトとして使用する場合は、CTEの最初の部分でNULLの代わりに_Top_Property_Key_を_Property_Key_に設定します。

_        ,m.Property_Key as Top_Property_Key
_
1
RDFozz