115

SQL Server で再帰的な自己結合を行う最も簡単な方法は何ですか? 次のようなテーブルがあります。

PersonID | Initials | ParentID
1          CJ         NULL
2          EB         1
3          MB         1
4          SW         2
5          YT         NULL
6          IS         5

そして、特定の人から始まる階層に関連するレコードのみを取得できるようにしたいと考えています。したがって、PersonID=1 で CJ の階層を要求すると、次のようになります。

PersonID | Initials | ParentID
1          CJ         NULL
2          EB         1
3          MB         1
4          SW         2

EB の場合は次のようになります。

PersonID | Initials | ParentID
2          EB         1
4          SW         2

私はこれに少しこだわっていますが、結合の束に基づく固定深度の応答とは別に、これを行う方法を考えることができません。レベルが多くないので、これはたまたまですが、適切に行いたいと思います。

ありがとう!クリス。

4

6 に答える 6

127
WITH    q AS 
        (
        SELECT  *
        FROM    mytable
        WHERE   ParentID IS NULL -- this condition defines the ultimate ancestors in your chain, change it as appropriate
        UNION ALL
        SELECT  m.*
        FROM    mytable m
        JOIN    q
        ON      m.parentID = q.PersonID
        )
SELECT  *
FROM    q

順序付け条件を追加することで、ツリーの順序を維持できます。

WITH    q AS 
        (
        SELECT  m.*, CAST(ROW_NUMBER() OVER (ORDER BY m.PersonId) AS VARCHAR(MAX)) COLLATE Latin1_General_BIN AS bc
        FROM    mytable m
        WHERE   ParentID IS NULL
        UNION ALL
        SELECT  m.*,  q.bc + '.' + CAST(ROW_NUMBER() OVER (PARTITION BY m.ParentID ORDER BY m.PersonID) AS VARCHAR(MAX)) COLLATE Latin1_General_BIN
        FROM    mytable m
        JOIN    q
        ON      m.parentID = q.PersonID
        )
SELECT  *
FROM    q
ORDER BY
        bc

条件を変更ORDER BYすることで、兄弟の順序を変更できます。

于 2009-11-18T16:35:13.123 に答える
26

CTEを使用すると、このようにすることができます

DECLARE @Table TABLE(
        PersonID INT,
        Initials VARCHAR(20),
        ParentID INT
)

INSERT INTO @Table SELECT     1,'CJ',NULL
INSERT INTO @Table SELECT     2,'EB',1
INSERT INTO @Table SELECT     3,'MB',1
INSERT INTO @Table SELECT     4,'SW',2
INSERT INTO @Table SELECT     5,'YT',NULL
INSERT INTO @Table SELECT     6,'IS',5

DECLARE @PersonID INT

SELECT @PersonID = 1

;WITH Selects AS (
        SELECT *
        FROM    @Table
        WHERE   PersonID = @PersonID
        UNION ALL
        SELECT  t.*
        FROM    @Table t INNER JOIN
                Selects s ON t.ParentID = s.PersonID
)
SELECT  *
FROm    Selects
于 2009-11-18T16:37:59.513 に答える
5

大きなテーブルの変更を伴う Quassnoi クエリ。10 よりも多くの子を持つ親: str(5) としてフォーマットし、row_number()

WITH q AS
        (
        SELECT m.*, CAST(str(ROW_NUMBER() OVER (ORDER BY m.ordernum),5) AS VARCHAR(MAX)) COLLATE Latin1_General_BIN AS bc
        FROM #tm
        WHERE 親 ID =0
        ユニオンオール
        SELECT m.*, q.bc + '.' + str(ROW_NUMBER() OVER (PARTITION BY m.ParentID ORDER BY m.ordernum),5) COLLATE Latin1_General_BIN
        FROM #tm
        参加する q
        ON m.parentID = q.DBID
        )
選択する *
からq
オーダーバイ
        紀元前

于 2009-11-18T17:42:15.047 に答える
2

SQL 2005 以降では、示されている例に従って、CTE が標準的な方法です。

SQL 2000、UDFを使用して実行できます-

CREATE FUNCTION udfPersonAndChildren
(
    @PersonID int
)
RETURNS @t TABLE (personid int, initials nchar(10), parentid int null)
AS
begin
    insert into @t 
    select * from people p      
    where personID=@PersonID

    while @@rowcount > 0
    begin
      insert into @t 
      select p.*
      from people p
        inner join @t o on p.parentid=o.personid
        left join @t o2 on p.personid=o2.personid
      where o2.personid is null
    end

    return
end

(これは 2005 年に動作しますが、それは標準的な方法ではありません。とはいえ、それがより簡単な方法であることがわかった場合は、それを実行してください)

SQL7 で本当にこれを行う必要がある場合は、sproc で上記の大まかに行うことができますが、そこから選択することはできません - SQL7 は UDF をサポートしていません。

于 2009-11-18T17:18:06.440 に答える