帮我找出一个MySQL查询

时间:2009-04-08 17:29:17

标签: 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

我正在尝试进行查询以获取其下没有任何Species的Class,Order和Family名称列表。您可以看到该表具有从Order一直到Species的某种形式的层次结构。每个表都有外键(FK),它与层次结构上面的直接表相关。

试图让这个工作,但我做得不好。 任何帮助将不胜感激!

4 个答案:

答案 0 :(得分:6)

元答案(对前两个答案的评论):

使用IN往往会降级为非常类似于IN中所有术语的OR(分离)的东西。表现不佳。

进行左连接并寻找null是一种改进,但它是蒙昧主义的。如果我们可以说出我们的意思,那么就让我们用自然语言说出来的一个最糟糕的说法:

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。

此外,如果一个家庭有许多属(并且在生物学中,大多数家庭都这样做),当我们关心的只是家庭时,我们将获得每行(家庭,属)一行。所以让我们每个家庭得一排:

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或析取对性能有害。您能详细说明一下,还是指向一个我可以了解更多不同数据库操作的相对性能成本的资源?

这样想。假设SQL中没有IN运算符。你怎么假装IN?

通过一系列OR:

where foo in (1, 2, 3)

相当于

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

好的,你说,但这仍然没有告诉我为什么这很糟糕。这很糟糕,因为通常没有合适的方法来使用密钥或索引进行查找。所以你得到的是a)表扫描,对于每个析取(或者是IN列表的谓词或元素),行进行测试,直到测试为真或列表用完为止。或者b)你得到每个这些析取的表扫描。第二种情况(b)实际上可能更好,这就是为什么你有时会看到一个带有OR的选择转换为OR联合的每个分支的一个选择:

 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列表转换为连接 - 而您给出的其他答案恰恰是最有可能的情况。

但是如果我们可以明确地将查询转换为连接,那么我们不必怀疑查询优化器是否是智能的。通常,连接是数据库最擅长的。

答案 1 :(得分:3)

嗯,只要给这个快速而肮脏的镜头,我会写这样的东西。我花了大部分时间使用Firebird,因此MySQL语法可能与 little 不同,但这个想法应该是明确的

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 )

如果你想强制执行某个属,那么你只需将连接的“左”部分从一个族移到另一个属。

我希望我不会误解这个问题,从而导致你走上错误的道路。祝你好运!

编辑:实际上,重读这个我认为这只会抓住一个属中没有物种的家庭。我想你也可以添加“和(g.id为空)”。

答案 2 :(得分:1)

选择救援......


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

答案 3 :(得分: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中最多选择一行。