web-dev-qa-db-fra.com

Obtenez des parents et des enfants de la structure de dossiers d'arbres dans mon SQL <8 et sans CTES

J'ai une table de dossiers qui se joint à lui-même sur un id, parent_id relation amoureuse:

CREATE TABLE folders (
  id int(10) unsigned NOT NULL AUTO_INCREMENT,
  title nvarchar(255) NOT NULL,
  parent_id int(10) unsigned DEFAULT NULL,
  PRIMARY KEY (id)
);

INSERT INTO folders(id, title, parent_id) VALUES(1, 'root', null);
INSERT INTO folders(id, title, parent_id) values(2, 'one', 1);
INSERT INTO folders(id, title, parent_id) values(3, 'target', 2);
INSERT INTO folders(id, title, parent_id) values(4, 'child one', 3);
INSERT INTO folders(id, title, parent_id) values(5, 'child two', 3);
INSERT INTO folders(id, title, parent_id) values(6, 'root 2', null);
INSERT INTO folders(id, title, parent_id) values(7, 'other child one', 6);
INSERT INTO folders(id, title, parent_id) values(8, 'other child two', 6);

Je veux une requête qui retourne tous les parents de cet enregistrement, retour à la route et à tous les enfants.

Donc, si je demande un dossier avec id=3, Je reçois des disques: 1, 2, 3, 4, 5. Je suis bloqué comment obtenir les parents.

La version de MySQL est de 5,7 et il n'y a pas de prévu immédiat de mettre à niveau si malheureusement, des CTES ne sont pas une option.

J'ai créé ceci SQL FIDDLE

15
dagda1

Remarque Ma solution est plus ou moins identique à @marc ALFF. N'a pas réalisé qu'il était déjà là avant de taper/préparation de la réponse dans un éditeur.

Il est très difficile d'obtenir une requête pour atteindre votre objectif (ou d'autres exigences typiques de l'ensemble de données hiérarchique) sans utilisation de CTES ou d'autres supports de requête hiérarchiques (par exemple précédent, connectez-vous à Oracle). C'était le pilote principal des bases de données pour proposer des CTES, etc.

Il y a beaucoup de nombreuses années, lorsque ce soutien à la modélisation des entités hiérarchiques n'était pas disponible dans des bases de données, les exigences décrites par vous et de nombreux autres éléments ont été résolus en modélisant de telles entités légèrement différemment.

Le concept est simple. En substance, deux autres attributs sont introduits dans la table hiérarchique (ou une table séparée étrangère saisie dans une table hiérarchique) appelée gauche_boundary et droit_boundary (appelez tout ce que vous souhaitez après tout ce qui est dans le nom). Pour chaque ligne, les valeurs (chiffres) de ces attributs sont choisis pour couvrir les valeurs de ces attributs pour tous leurs enfants. En d'autres termes, les frontières gauche et droite d'un enfant seront entre les frontières gauche et droite de ses parents.

À titre d'exemple

enter image description here

Créer cette hiérarchie utilisée pour faire partie d'un travail de lot tôt le matin ou des frontières ont été choisies si largement séparées pendant la période de conception qu'ils couvraient facilement toutes les profondeurs d'arbre.

Je vais utiliser cette solution pour atteindre votre objectif. Tout d'abord, je vais introduire une deuxième table (aurait pu introduire les attributs dans la même table, décidé de ne pas perturber votre modèle de données)

CREATE TABLE folder_boundaries (
  id int(10) unsigned NOT NULL AUTO_INCREMENT,
  folder_id int(10) unsigned NOT NULL,
  left_boundary int(10) unsigned,
  right_boundary int(10) unsigned,
  PRIMARY KEY (id),
  FOREIGN KEY (folder_id) REFERENCES folders(id)
);

Les données de ce tableau basées sur votre ensemble de données

NSERT INTO folder_boundaries(folder_id, left_boundary, right_boundary) VALUES(1, 1, 10);
INSERT INTO folder_boundaries(folder_id, left_boundary, right_boundary) VALUES(2, 2, 9);
INSERT INTO folder_boundaries(folder_id, left_boundary, right_boundary) VALUES(3, 3, 8);
INSERT INTO folder_boundaries(folder_id, left_boundary, right_boundary) VALUES(4, 4, 4);
INSERT INTO folder_boundaries(folder_id, left_boundary, right_boundary) VALUES(5, 4, 4);
INSERT INTO folder_boundaries(folder_id, left_boundary, right_boundary) VALUES(6, 21, 25);
INSERT INTO folder_boundaries(folder_id, left_boundary, right_boundary) VALUES(7, 22, 22);
INSERT INTO folder_boundaries(folder_id, left_boundary, right_boundary) VALUES(7, 22, 22);

Voici la requête pour atteindre ce que vous êtes après

select f.id, f.title
from folders f
join folder_boundaries fb on f.id = fb.folder_id
where fb.left_boundary < (select left_boundary from folder_boundaries where folder_id = 3)
and fb.right_boundary > (select right_boundary from folder_boundaries where folder_id = 3)
union all
select f.id, f.title
from folders f
join folder_boundaries fb on f.id = fb.folder_id
where fb.left_boundary >= (select left_boundary from folder_boundaries where folder_id = 3)
and fb.right_boundary <= (select right_boundary from folder_boundaries where folder_id = 3)

Résultat

enter image description here

1
Gro

Vous pouvez effectuer une union entre des rangées parent et des lignes d'enfants comme celle-ci:

select title, id, @parent:=parent_id as parent from
               (select @parent:=3 ) a join (select * from folders order by id desc) b where @parent=id
union select title, id, parent_id as parent from folders where  parent_id=3 ORDER BY id

ici un échantillon dbfiddle

0
Abdelkarim EL AMEL

Supposons que vous sachiez la profondeur maximale de l'arbre, vous pouvez "créer" une boucle pour obtenir ce que vous voulez:

Obtenez des nœuds parents:

SELECT  @id :=
        (
        SELECT  parent_id
        FROM    folders
        WHERE   id = @id
        ) AS folderId, vars.id
FROM    (
        SELECT  @id := 7 AS id
        ) vars
INNER JOIN (
    SELECT 0 AS nbr UNION ALL SELECT 1 UNION ALL SELECT 2 
 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 
 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 
 UNION ALL SELECT 9) temp
WHERE @id IS NOT NULL

Obtenez des nœuds d'enfants:

SELECT  @id :=
        (
        SELECT  GROUP_CONCAT(id)
        FROM    folders
        WHERE   FIND_IN_SET(parent_id, @id)
        ) AS folderIds, vars.id
FROM    (
        SELECT  @id := 1 AS id
        ) vars
INNER JOIN (
    SELECT 0 AS nbr UNION ALL SELECT 1 UNION ALL SELECT 2 
 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 
 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 
 UNION ALL SELECT 9) temp
WHERE @id IS NOT NULL

Cela fonctionne par

  • Créer une jointure entre une sous-requête variable statique (SELECT @id := 1 AS id) et un ensemble statique de 10 lignes dans ce cas (profondeur maximale)
  • utilisation d'une sous-requête dans la sélection pour traverser l'arborescence et trouver tous les parents ou les nœuds d'enfants

Le but de la jointure est de créer un ensemble de résultats de 10 lignes, de sorte que la sous-requête dans la sélection est exécutée 10 fois.

Alternativement, si vous ne connaissez pas la profondeur maximale, vous pouvez remplacer la sous-requête jointe avec

INNER JOIN (
SELECT 1 FROM folder) temp

ou afin d'éviter tout le syndicat, utilisez une limite:

INNER JOIN (
SELECT 1 FROM folder LIMIT 100) temp

Références: - requêtes hiérarchiques dans MySQL

0
Jannes Botis