2009-04-08 4 views
0

Ce sont des tableaux que j'ai:Aidez-moi à trouver une requête MySQL

 
Class 
- id 
- name 

Order 
- id 
- name 
- class_id (FK) 

Family 
- id 
- order_id (FK) 
- name 

Genus 
- id 
- family_id (FK) 
- name 

Species 
- id 
- genus_id (FK) 
- name 

Je suis en train de faire une requête pour obtenir une liste de classe, l'ordre, et les noms de famille qui n'a pas toutes les espèces en leur. Vous pouvez voir que la table a une forme de hiérarchie allant de Order jusqu'à Espèce. Chaque table a Foreign Key (FK) qui se rapporte à la table immédiate au-dessus de lui-même sur la hiérarchie.

Essayer d'obtenir ceci au travail, mais je ne fais pas si bien. Toute aide serait appréciée!

+0

[Tables de taxonomie dans mysql] (http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/) excellent article sur ce sujet. [StackOverFlow] (http://stackoverflow.com/questions/4048151/what-are-the-options-for-storing-hierarchical-data-in-a-relational-database) - quelles-sont-les-options-pour -storing-hierarchical-data-in-a-based-database – rd42

Répondre

3

Eh bien, juste en donnant un coup rapide et sale, j'écrirais quelque chose comme ça. Je passe la plupart de mon temps à l'aide Firebird donc la syntaxe MySQL peut être un peu différent, mais l'idée devrait être clair

select f.name 
from family f left join genus g on f.id = g.family_id 
     left join species s on g.id = species.genus_id 
where (s.id is null) 

si vous voulez appliquer l'existence d'un genre alors vous supprimez la « gauche » partie de la jointure de la famille au genre. J'espère que je ne me méprends pas sur la question et, par conséquent, que je ne suis pas dans la bonne direction. Bonne chance!

edit: En fait, en relisant cela je pense que cela va juste attraper les familles où il n'y a pas d'espèces dans un genre. Vous pourriez ajouter un "et (g.id est nul)" aussi, je pense.

1

Sous-sélection à la rescousse ...


select f.name from family as f, genus as g 
where 
    f.id == g.family_id and 
    g.id not in (select genus_id from species); 
1
SELECT f.name 
FROM family f 
WHERE NOT EXISTS (
     SELECT 1 
     FROM genus g 
     JOIN species s 
     ON  g.id = s.genus_id 
     WHERE g.family_id = f.id 
     ) 

Notez que contrairement pur LEFT JOIN solutions, ce qui est plus efficace.

Il ne sélectionne pas TOUTES lignes en filtrant celles ayant les valeurs NOT NULL, mais sélectionne au maximum une ligne parmi genus et species.

+0

Étiez-vous et moi séparés à la naissance, Quassnoi? – tpdi

6

Meta-réponse (commentaire sur les deux réponses précédentes):

L'utilisation IN tend à se dégrader à quelque chose comme un OU (disjonction) de tous les termes du IN. Mauvaise performance.

Faire une jointure à gauche et chercher null est une amélioration, mais c'est obscurantiste. Si nous pouvons dire ce que nous entendons, disons dans un Wau qui est clossest à la façon dont nous dirions en langage naturel:

select f.name 
from family f left join genus g on f.id = g.family_id 
     WHERE NOT EXISTS (select * from species c where c.id = g.id); 

Nous voulons où quelque chose n'existe pas, donc si on peut dire « où n'existe pas "tant mieux. Et, le select * dans la sous-requête ne signifie pas qu'il ramène vraiment une rangée entière, donc ce n'est pas une "optimisation" pour remplacer select * avec select 1, au moins pas sur n'importe quel SGBDR moderne. En outre, lorsqu'une famille a de nombreux genres (et en biologie, la plupart des familles le font), nous obtiendrons une rangée par (famille, genre) alors que tout ce qui nous intéresse est la famille. Donc, obtenons une ligne par famille:

select DISTINCT f.name 
from family f left join genus g on f.id = g.family_id 
     WHERE NOT EXISTS (select * from species c where c.id = g.id); 

Ceci n'est pas encore optimal. Pourquoi? Eh bien, il répond à l'exigence du PO, en ce qu'il trouve des genres "vides", mais il ne parvient pas à trouver des familles qui n'ont pas de genre, des familles "vides". Pouvons-nous le faire faire aussi?Nous pouvons même nous débarrasser du distinct, parce que nous ne joignons pas la famille à quoi que ce soit. Et que est une optimisation.

Commentaire de OP:

Ce fut une explication très lucide. Cependant, je suis curieux de savoir pourquoi utiliser IN ou disjunctions est mauvais pour la performance. Pouvez-vous élaborer sur ce point ou me diriger vers une ressource où je peux en apprendre davantage sur le coût relatif de la performance de différentes opérations de DB?

Pensez-y de cette façon. Dites qu'il n'y avait pas d'opérateur IN dans SQL. Comment feriez-vous un faux?

Par une série de ORS:

where foo in (1, 2, 3) 

est équivalent à

where (foo = 1) or (foo = 2) or (foo = 3) 

Ok, vous dites, mais cela ne me dit toujours pas pourquoi il est mauvais. C'est mauvais parce qu'il n'y a souvent pas de moyen décent d'utiliser une clé ou un index pour chercher ça. Donc, ce que vous obtenez est soit a) un balayage de table, où pour chaque disjonction (prédicat or'd ou élément d'une liste IN), la ligne est testée, jusqu'à ce qu'un test soit vrai ou que la liste soit épuisée. Ou b) vous obtenez un balayage de table pour chacune de ces disjonctions. Dans le second cas (b) peut effectivement être mieux, ce qui est la raison pour laquelle vous voyez parfois une sélection avec un OU transformé en un sélectionner pour chaque étape de la OU union'd ensemble:

select * from table where x = 1 or x = 3 ; 

select * from table where x = 1 
union select * from table where x = 3 ; 

Maintenant, cela ne veut pas dire vous ne pouvez jamais utiliser une liste OU ou une liste IN. Et dans certains cas, l'optimiseur de requête est assez intelligent pour transformer une liste IN en une jointure - et les autres réponses que vous avez reçues sont précisément les cas où cela est le plus probable. Mais si nous pouvons explicitement transformer notre requête en jointure, eh bien, nous n'avons pas à nous demander si l'optimiseur de requête est intelligent. Et en général, les jointures sont ce que la base de données est le mieux à faire.

+0

C'était une explication très claire. Cependant, je suis curieux de savoir pourquoi utiliser IN ou disjunctions est mauvais pour la performance. Pouvez-vous élaborer sur ce point ou me diriger vers une ressource où je peux en apprendre davantage sur le coût relatif de la performance de différentes opérations de DB? – Calvin