Quel est le moyen le plus simple de faire une auto-jointure récursive dans SQL Server? J'ai une table comme celle-ci:
PersonID | Initials | ParentID
1 CJ NULL
2 EB 1
3 MB 1
4 SW 2
5 YT NULL
6 IS 5
Et je veux pouvoir obtenir les enregistrements uniquement liés à une hiérarchie commençant par une personne spécifique. Donc, si je demandais la hiérarchie de CJ par PersonID = 1, j'obtiendrais:
PersonID | Initials | ParentID
1 CJ NULL
2 EB 1
3 MB 1
4 SW 2
Et pour les EB, j'aurais:
PersonID | Initials | ParentID
2 EB 1
4 SW 2
Je suis un peu coincé là-dessus. Je ne vois pas comment le faire, à part une réponse à profondeur fixe basée sur un ensemble de jointures. Cela ferait ce qu'il se passe car nous n'aurons pas beaucoup de niveaux mais j'aimerais le faire correctement.
Merci! Chris.
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
En ajoutant la condition de commande, vous pouvez préserver l'ordre de l'arbre:
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
En modifiant la condition ORDER BY
, vous pouvez modifier l’ordre des frères et sœurs.
En utilisant les CTE, vous pouvez le faire de cette façon
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
La requête Quassnoi avec une modification pour la grande table. Parents avec plus d'enfants que 10: Formater comme str (5) le row_number ()
AVEC q AS ( SELECT m. *, CAST (str (ROW_NUMBER () OVER (ORDER BY m.ordernum)), 5) AS VARCHAR (MAX)) COLLATE Latin1_General_BIN AS bc FROM #t m WHERE ParentID = 0 UNION ALL SELECT m. *, Q.bc + '.' + Str (ROW_NUMBER () OVER (PARTITION DE m.ParentID ORDER BY m.ordernum), 5) COLLATE Latin1_General_BIN FROM # t m JOIN q ON m.parentID = q.DBID ) SELECT * FROM q COMMANDER PAR avant JC
SQL 2005 ou version ultérieure, les CTE sont le moyen standard d’utiliser les exemples présentés.
SQL 2000, vous pouvez le faire en utilisant des 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
(ce qui fonctionnera en 2005, ce n’est tout simplement pas la façon habituelle de le faire. Cela dit, si vous trouvez que la façon la plus facile de travailler est de fonctionner avec elle)
Si vous avez vraiment besoin de faire cela dans SQL7, vous pouvez effectuer à peu près ce qui précède dans un sproc mais vous ne pouvez pas en sélectionner un - SQL7 ne prend pas en charge les fichiers UDF.
Vérifiez les éléments suivants pour vous aider à comprendre le concept de récursion CTE
DECLARE
@startDate DATETIME,
@endDate DATETIME
SET @startDate = '11/10/2011'
SET @endDate = '03/25/2012'
; WITH CTE AS (
SELECT
YEAR(@startDate) AS 'yr',
MONTH(@startDate) AS 'mm',
DATENAME(mm, @startDate) AS 'mon',
DATEPART(d,@startDate) AS 'dd',
@startDate 'new_date'
UNION ALL
SELECT
YEAR(new_date) AS 'yr',
MONTH(new_date) AS 'mm',
DATENAME(mm, new_date) AS 'mon',
DATEPART(d,@startDate) AS 'dd',
DATEADD(d,1,new_date) 'new_date'
FROM CTE
WHERE new_date < @endDate
)
SELECT yr AS 'Year', mon AS 'Month', count(dd) AS 'Days'
FROM CTE
GROUP BY mon, yr, mm
ORDER BY yr, mm
OPTION (MAXRECURSION 1000)