在自引用表中获取给定父 ID 的所有子记录

问题描述 投票:0回答:3

我有一个引用自身的表,我需要能够从给定的父 ID 中选择父记录及其所有子记录。

我的桌子如下:

ID   | ParentID | Name         
-----------------------              
1      NULL       A
2      1          B-1
3      1          B-2
4      2          C-1
5      2          C-2

因此,对于上面的示例,我希望能够传入值 1 并获取上面的所有记录。

到目前为止,我已经提出了以下递归表值函数,但它的行为不符合预期(仅返回第一条记录)。

CREATE FUNCTION [dbo].[SelectBranches]
(   
    @id INT
    ,@parentId INT
)
RETURNS @branchTable TABLE
(
    ID INT
    ,ParentID INT
    ,Name INT
)
AS
BEGIN 

    IF @branchId IS NOT NULL BEGIN

        INSERT INTO @branchTable
        SELECT 
            ID
            ,ParentID
            ,Name
        FROM
            tblLinkAdvertiserCity
        WHERE
            ID = @id

    END

    INSERT INTO @branchTable
    SELECT
        br.ID
        ,br.ParentID
        ,br.Name
    FROM
        @branchTable b
    CROSS APPLY
        dbo.SelectBranches(NULL, b.ParentID) br

    RETURN
END
GO
sql sql-server-2005 recursion hierarchy
3个回答
72
投票

你可以试试这个

DECLARE @Table TABLE(
        ID INT,
        ParentID INT,
        NAME VARCHAR(20)
)

INSERT INTO @Table (ID,ParentID,[NAME]) SELECT 1, NULL, 'A'
INSERT INTO @Table (ID,ParentID,[NAME]) SELECT 2, 1, 'B-1'
INSERT INTO @Table (ID,ParentID,[NAME]) SELECT 3, 1, 'B-2'
INSERT INTO @Table (ID,ParentID,[NAME]) SELECT 4, 2, 'C-1'
INSERT INTO @Table (ID,ParentID,[NAME]) SELECT 5, 2, 'C-2'


DECLARE @ID INT

SELECT @ID = 2

;WITH ret AS(
        SELECT  *
        FROM    @Table
        WHERE   ID = @ID
        UNION ALL
        SELECT  t.*
        FROM    @Table t INNER JOIN
                ret r ON t.ParentID = r.ID
)

SELECT  *
FROM    ret

-1
投票

CTE

中的递归看起来有点昂贵,所以我编写了这个函数,它利用递归函数调用,但比
CTE
递归快得多。

CREATE FUNCTION [dbo].[Fn_GetSubCategories] ( @p_ParentCategoryId INT ) RETURNS @ResultTable TABLE ( Id INT ) AS BEGIN --Insert first level subcategories. INSERT INTO @ResultTable SELECT Id FROM Category WHERE ParentCategoryId = @p_ParentCategoryId OR Id = @p_ParentCategoryId DECLARE @Id INT DECLARE @ParentCategory TABLE(Id INT) DECLARE cur_categories CURSOR LOCAL STATIC READ_ONLY FORWARD_ONLY FOR SELECT Id FROM Category WHERE ParentCategoryId = @p_ParentCategoryId and Id != @p_ParentCategoryId OPEN cur_categories IF @@CURSOR_ROWS > 0 BEGIN FETCH NEXT FROM cur_categories INTO @Id WHILE @@FETCH_STATUS = 0 BEGIN --Insert remaining level sub categories. IF EXISTS(SELECT 1 FROM Category WHERE ParentCategoryId = @Id AND Id != @Id) BEGIN INSERT INTO @ResultTable SELECT DISTINCT C.Id from Fn_GetSubCategories(@Id) C INNER JOIN @ResultTable R ON C.Id != R.Id END FETCH NEXT FROM cur_categories INTO @Id END --Delete duplicate records ;WITH CTE AS (SELECT *,ROW_NUMBER() OVER (PARTITION BY Id ORDER BY Id) AS RN FROM @ResultTable) DELETE FROM CTE WHERE RN<>1 END CLOSE cur_categories DEALLOCATE cur_categories RETURN END
    

-2
投票
除非您使用的是Oracle,否则您的表结构不适合所描述的问题。您尝试做的是获取层次结构(遍历树结构)。

有一篇文章,

SQL 中的更多树和层次结构,描述了解决层次结构问题的一种方法。他基本上为每一行添加了一个描述层次结构的“血统”列。

© www.soinside.com 2019 - 2024. All rights reserved.