2009-04-02 8 views
1

J'ai une liste d'éléments dans MySQL, connectés via une colonne "parent_id".Supprimer des éléments orphelins dans une hiérarchie

Supposons que les colonnes sont: id, nom, PARENT_ID

Si l'un de mes utilisateurs supprime un élément haut dans la hiérarchie, je dois supprimer tous ses enfants. Donc, deux partie de la question:

1) Existe-t-il un appel MySQL efficace et efficace qui retournera les ID pour tous les éléments dont le parent n'existe plus?

2) En PHP, je pense que je peux obtenir ces ID orphelins de MySQL dans un tableau, puis exécuter une boucle foreach et supprimer chacun?

Merci beaucoup pour votre aide.

Répondre

1

Je suppose que ce que l'auteur original avait à l'esprit quelque chose dans les lignes de requêtes hiérarchiques.

Malheureusement, MySQL n'a pas de support natif pour les requêtes hiérarchiques (contrairement, par exemple, à Oracle, où vous pouvez utiliser CONNECT BY pour obtenir ce que vous voulez).

Probablement la meilleure façon de supprimer tous les orphelins serait d'exécuter une requête comme:

 
    SELECT t1.id 
    FROM table t1 
     LEFT JOIN table t2 ON t2.id = t1.parent_id 
    WHERE t2.id IS NULL 

Cela vous donne toutes les lignes de table où leur parent n'existe pas. Associez-le à un script PHP qui continue à exécuter la requête et à supprimer les résultats. Après deux itérations, votre table doit être exempte d'orphelins (cette opération peut probablement être fusionnée en une seule instruction DELETE que vous pourriez exécuter dans une boucle while).

Vous devez exécuter select-delete plusieurs fois à cause de la transitivité - considérez une situation où un orphelin est parent d'un autre enregistrement; Avec la première itération, vous retireriez le premier orphelin, rendant orphelin le prochain album de la chaîne. De même, assurez-vous d'ignorer explicitement la tête de votre hiérarchie, sinon vous finirez avec une table vide (car la tête est, par définition, orpheline).

4

Si vous utilisez InnoDB, vous devez rechercher dans Foreign Key Constraints qui prend soin de cela pour vous en utilisant l'option ON DELETE CASCADE lorsque vous définissez vos relations de clé étrangère.

Un exemple de la documentation:

CREATE TABLE parent (id INT NOT NULL, 
        PRIMARY KEY (id)) ENGINE=INNODB; 

CREATE TABLE child (id INT, parent_id INT, 
        INDEX par_ind (parent_id), 
        FOREIGN KEY (parent_id) REFERENCES parent(id) 
        ON DELETE CASCADE) ENGINE=INNODB; 

Avec cela en place, si vous deviez faire ajouter un parent et une rangée d'enfants correspondant à ceci:

INSERT INTO parent (id) VALUES (1); 
INSERT INTO child (id, parent_id) VALUES (1,1); 

Et puis retiré le parent comme ceci:

DELETE FROM parent WHERE id = 1; 

Vous trouverez l'enregistrement enfant correspondant à être parti. C'est, à mon avis, la meilleure façon de le faire.

EDIT: Pour le faire dans une table, vous feriez quelque chose comme ceci:

CREATE TABLE parent (
    id INT NOT NULL, 
    name varchar(250) not null, 
    parent_id INT NULL, 
    FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE, 
    PRIMARY KEY (id) 
) ENGINE=INNODB 

Ensuite, si vous ajoutez deux lignes, une référençant l'autre:

INSERT INTO parent (id,name,parent_id) 
VALUES ('1', 'Test 1', NULL), ('2', 'Test 2', '1') 

Ensuite, supprimez la rangée parente des deux:

DELETE FROM parent WHERE id = 1; 

Vous allez Je trouve qu'il supprime la ligne enfant avec parent_id de 1.

+0

Tous mes articles sont dans une seule table. – jmccartie

+0

Cela fonctionne toujours avec les relations dans une seule table. Je vais ajouter un exemple dans une seconde. –

1

RIGHT JOINS sont généralement utilisés pour trouver des orphelins dans d'autres bases de données. Mais c'est possible aussi avec un LEFT JOIN.

Here is très bien layed sur l'article à obtenir des lignes orphelines dans MySQL

+0

Mes articles sont dans une seule table. Est-ce que je rejoindrais moi-même? – jmccartie

+0

Vous pouvez vous rejoindre oui. –

1

Une autre option (si vous n'utilisez pas InnoDB, +1 Paolo) est d'utiliser Delete Triggers sur la table MySQL parent. N'oubliez pas que lorsque vous utilisez un déclencheur de suppression, la ligne de suppression est référencée par OLD. Voici un exemple de déclencheur.

DELIMITER // 

CREATE TRIGGER delete_clean BEFORE DELETE ON Parent 
FOR EACH ROW 
BEGIN 
    DELETE * FROM Parent WHERE Parent.parent_id = OLD.id; 
END // 

DELIMITER ; 
2

Si vos données est linéaire hiérarchique (une seule relation parent par nœud), et haute lecture, écriture faible, vous pouvez envisager la migration à une modification Précommande structure arborescente Traversal. Cela semble effrayant, mais c'est en fait très soigné. Il y a un grand article dessus ici: http://www.sitepoint.com/article/hierarchical-data-database/1/

Non seulement la récupération des données d'une telle structure est extrêmement efficace (SELECT simple et vous obtenez tous les enfants), vous serez en mesure de supprimer une branche entière de l'arbre dans un seule instruction DELETE.

Questions connexes