2009-04-08 23 views
0

はこれらのテーブルにあるMySQLのクエリを把握ヘルプそれら。テーブルにはOrderからSpeciesまでの階層構造があります。各テーブルには、階層上のそれ自身の上の直接テーブルに関連する外部キー(FK)があります。私は私が持っている

これを実現しようとしていますが、うまくやっていません。 ご協力いただければ幸いです!

+0

[mysqlの分類表](http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/)これに関する素晴らしい記事。 [StackOverFlow](http://stackoverflow.com/questions/4048151/what-are-the-options-for-storing-hierarchical-data-in-a-relational-database) - what-are-the-options-for階層構造のリレーショナルデータベースのデータ – rd42

答えて

3

これをすばやく汚れたショットにするだけで、このようなことを書くことができます。私は、MySQLの構文は少し異なる場合がありますので、火の鳥を使用して私の時間の大半を費やしていますが、属はその後、あなただけ削除されてそこに強制したい場合にアイデアが

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) 

明確にする必要があり、「左」ファミリーから属への参加の一部。

私は質問を誤解していないと思いますので、間違った道を導いてくれることを願っています。がんばろう!

編集:実際には、これは私が属の中に種がない家族を捕まえてくれると思います。 "and(g.id is null)"を追加することもできます。

1

副SELECT救助に...


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 
     ) 

注意、純粋LEFT JOINソリューションとは異なり、これは、より効率的であるより。

NOT NULLの値を除外するすべての行を選択するのではなく、genusspeciesから最大で1行を選択します。

+0

あなたと私は出産時に分かれていましたか? – tpdi

6

メタ答(前の2つの回答にコメント):

inを使用しては非常に、IN内のすべての用語のOR(論理和)のようなものに劣化する傾向があります。パフォーマンスが悪い

左結合を実行してnullを検索することは改善ですが、それは隠喩的です。我々は我々が何を意味するかと言うことができれば、我々は自然言語でそれを言うと思いますどのようにclossestだWAUでそれを言わせて:

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); 

何かが存在しない場合には我々がしたいので、私たちはどこ」と言うことができるかどうか存在していない "と言いました。そして、サブクエリ内のselect *は実際に行全体を戻しているわけではないので、select *select 1に置き換える "最適化"ではなく、現代のRDBMSには少なくともありません。

さらに、家族が多くの属(生物学では、ほとんどの家族が行う)がある場合、私たちが気にするものが家族である場合、1つの行(家族、属)が得られます。したがって、1家族につき1行を取得しましょう:

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); 

これはまだ最適ではありません。どうして?それは「空」の属を見つける点でOPの要件を満たすが、それは一般的でない「空の」家族のない家族を見つけることができない。私たちもそれをすることができますか?

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

私たちは家族に何かに参加していないので、私たちは別のものを取り除くことさえできます。そのです。 OPから

コメント:

非常に明快な説明でした。しかし、なぜ私はINまたはdisjunctionsを使用することがパフォーマンスに悪いのか不思議です。それについて詳しく説明したり、私がさまざまなDB操作の相対的なパフォーマンスコストについてもっと知ることができるリソースを教えてください。

このように考えてください。 SQLにIN演算子がないとします。どのようにINを偽造しますか? ORの一連

where foo in (1, 2, 3) 

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

に相当し[OK]を、あなたが言うが、それは悪いですなぜそれはまだ私に教えてくれありません。キーやインデックスを使用してこれを調べる方法がしばしばないため、悪いことです。ですから、あなたが得るのは、a)テーブルスキャンで、各論理和(またはINリストの要素)に対して、テストが真であるか、リストが使い果たされるまで、行がテストされます。または、b)これらの論理和のそれぞれに対して表スキャンを取得します。あなたは時々ORとの選択を参照してください理由である(b)は実際には良いかもしれ後者の場合は、一つになって、または一緒union'dの各脚を選択:

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

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

を今、これは言っているわけではありませんORまたはINリストを決して使用することはできません。場合によっては、クエリオプティマイザはINリストを結合に変換するほどスマートであり、他の回答は、その可能性が最も高いケースです。

しかし、明示的にクエリを結合にすることができれば、クエリオプティマイザがスマートであるかどうか疑問に思う必要はありません。一般的に、ジョインとはデータベースがデータを処理するのに最適なものです。

+0

これは非常に明快な説明でした。しかし、なぜ私はINまたはdisjunctionsを使用することがパフォーマンスに悪いのか不思議です。それについて詳しく説明したり、私がさまざまなDB操作の相対的なパフォーマンスコストについてもっと知ることができるリソースを教えてください。 – Calvin