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
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
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
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.