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
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
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
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
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
(SELECT @id := 1 AS id)
et un ensemble statique de 10 lignes dans ce cas (profondeur maximale)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