web-dev-qa-db-fra.com

SQL Server: comment obtenir tous les enregistrements enfants avec un ID parent dans une table auto-référencée

Salut, j'ai une table qui se référence et j'ai besoin de pouvoir sélectionner le parent et tous ses enregistrements enfants à partir d'un ID parent donné.

Ma table est la suivante:

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

Donc, pour l'exemple ci-dessus, j'aimerais pouvoir passer une valeur de 1 et obtenir tous les enregistrements ci-dessus.

Jusqu'à présent, j'ai trouvé la fonction de valeur de table récursive suivante, mais elle ne se comporte pas comme prévu (renvoyant uniquement le premier enregistrement).

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
40
Matthew Dresser

Vous pouvez essayer ceci

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
61
Adriaan Stander

La récursivité dans CTE semble un peu chère, j'ai donc écrit cette fonction qui utilise l'appel de fonction récursive mais beaucoup plus rapidement que la récurrence 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
0
Ankush Madankar

Sauf si vous utilisez Oracle, la structure de votre table ne convient pas au problème décrit. Ce que vous essayez de faire, c'est de saisir une hiérarchie (en parcourant une structure arborescente).

Il y a un article, More Trees & Hierarchies in SQL , qui décrit une méthode de résolution du problème de hiérarchie. Il ajoute essentiellement une colonne "lignage" décrivant la hiérarchie à chaque ligne.

0