web-dev-qa-db-fra.com

Sélectionner tous les parents ou les enfants dans la même relation de table SQL Server

Développeurs SQL, j'ai pour tâche d'en apprendre beaucoup sur SQL Server 2012 sur une base de données mal planifiée.

SO, il y a la table Elem:

+-----------+----+---+----------+------------+
|VERSION(PK)|NAME|KEY|PARENT_KEY|DIST_KEY(FK)|
+-----------+----+---+----------+------------+
|1          |a   |12 |NULL      |1           |
+-----------+----+---+----------+------------+
|2          |b   |13 |12        |1           |
+-----------+----+---+----------+------------+
|3          |c   |14 |13        |1           |
+-----------+----+---+----------+------------+
|4          |d   |15 |12        |1           |
+-----------+----+---+----------+------------+
|5          |e   |16 |NULL      |1           |
+-----------+----+---+----------+------------+
|6          |e   |17 |NULL      |2           |
+-----------+----+---+----------+------------+

Après la mise à jour de la ligne, je dois vérifier la clé parent de l’élément afin de ne pas autoriser cet élément à être autogéré ou quelque chose du genre. 

Et lorsque je supprime la ligne, je dois supprimer tous les enfants et les enfants d’enfants, etc.

Les questions sont:

  1. Comment puis-je sélectionner tous les "parents + grands-parents + etc" d'un élément de DIST?

  2. Comment puis-je sélectionner tous les "fils + petits-fils + etc" d'un élément de DIST?

J'ai lu des articles sur les solutions avec CTE, mais je n'ai aucune racine d'éléments et je ne comprends même pas comment utiliser CTE à ce moment-là. 

S'il vous plaît, aidez!

Merci.

15
Khronos

J'ai rencontré ce problème , J'ai résolu le problème de cette façon

 --all  "parent + grandparent + etc" @childID Replaced with the ID you need

with tbParent as
(
   select * from Elem where [KEY]=@childID
   union all
   select Elem.* from Elem  join tbParent  on Elem.[KEY]=tbParent.PARENT_KEY
)
 SELECT * FROM  tbParent
 --all "sons + grandsons + etc" @parentID Replaced with the ID you need

with tbsons as
(
  select * from Elem where [KEY]=@parentID
  union all
  select Elem.* from Elem  join tbsons  on Elem.PARENT_KEY=tbsons.[KEY]
)
SELECT * FROM tbsons

PS.Mon anglais n'est pas bon.

23
HunkHui

voici une requête récursive vous donnant tous les ancêtres et tous les descendants d'un élément. Utilisez-les ensemble ou séparez-les en fonction de la situation. Remplacez les clauses where pour obtenir l'enregistrement souhaité. Dans cet exemple, je cherche la clé 13 (il s’agit de l’élément portant le nom = b) et trouve son ancêtre 12/a et son descendant 14/c.

with all_ancestors(relation, version, name, elem_key, parent_key, dist_key)
as 
(
  -- the record itself
  select 'self      ' as relation, self.version, self.name, self.elem_key, self.parent_key, self.dist_key
  from elem self
  where elem_key = 13
  union all
  -- all its ancestors found recursively
  select 'ancestor  ' as relation, parent.version, parent.name, parent.elem_key, parent.parent_key, parent.dist_key
  from elem parent
  join all_ancestors child on parent.elem_key = child.parent_key
)
, all_descendants(relation, version, name, elem_key, parent_key, dist_key)
as 
(
  -- the record itself
  select 'self      ' as relation, self.version, self.name, self.elem_key, self.parent_key, self.dist_key
  from elem self
  where elem_key = 13
  union all
  -- all its descendants found recursively
  select 'descendant' as relation, child.version, child.name, child.elem_key, child.parent_key, child.dist_key
  from elem child
  join all_descendants parent on parent.elem_key = child.parent_key
)
select * from all_ancestors
union
select * from all_descendants
order by elem_key
;

Voici le violon SQL: http://sqlfiddle.com/#!6/617ee/28 .

1
Thorsten Kettner

J'ai créé une fonction permettant de rechercher les parents d'un enfant spécifique où vous devez passer l'identifiant de l'enfant.

Ceci retournera la liste des parents sous forme de chaîne séparée par des virgules ..__ Essayez ceci si cela fonctionne pour vous.

Je suppose que le parent_key with null value est la racine.

CREATE FUNCTION checkParent(@childId INT)
RETURNS VARCHAR(MAX)
AS
BEGIN
    DECLARE @parentId VARCHAR(MAX) = NULL
    DECLARE @parentKey INT = null
    SET @parentId = (SELECT parent_key FROM Elem WHERE [KEY] = @childId)

    WHILE(@parentKey IS NOT NULL)
    begin
        SET @parentId = @parentId +  ', ' + (SELECT parent_key FROM Elem WHERE [KEY] = @parentId)
        SET @parentKey = (SELECT parent_key FROM Elem WHERE [KEY] = @parentId)
    END
    RETURN @parentId
END
GO
0
Suraj Shrestha

Je ne pense pas que cela puisse être fait d’une seule manière dans tous les cas, de sorte que vous puissiez sélectionner tous les parents, grands-parents, .... Une façon de le faire est de joindre elem table à elle-même, et cela dépend du nombre de niveaux de jointure que vous faites, du niveau de vos enfants, de vos petits-enfants que vous obtiendrez.

La solution peut être quelque chose comme ça (pour le deuxième cas)

cela vous sélectionnera tous les parents, enfants et petits-enfants

Select 
parent.key as parent_key,
child.key as child_key,
grandchild.key as grandchild_key 
from elem parent 
join elem child on (elem.key=child.parentkey)
join elem grandchild on (child.key=grandchild.parentkey)
where parent.parentkey is null; -- this make you sure that first level will be parents

la solution pour le premier cas est simplement que vous allez connecter des tables non pas dans le style 'clé = clé =' mais opposées 'parentkey = clé'. 

0
Ján Srniček