2009-04-08 2 views
0

Эти таблицы у меня есть:Помогите выяснить запрос 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 

Я пытаюсь сделать запрос, чтобы получить список класса, порядок и имена семьи, которые не имеют каких-либо видов в рамках их. Вы можете видеть, что таблица имеет некоторую форму иерархии от Ордера вплоть до видов. Каждая таблица имеет внешний ключ (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) - что-есть-опции для -storing-hierarchical-data-in-a-relational-database – rd42

ответ

3

Ну, просто давая этому быстрый и грязный снимок, я бы написал что-то вроде этого. Я провожу большую часть своего времени с использованием Firebird, так что синтаксис 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) 

, если вы хотите обеспечить, чтобы там быть родом, то вы просто удалить «левые» часть соединения от семьи к роду.

Надеюсь, я не ошибаюсь в вопросе и, таким образом, веду вас к неправильному пути. Удачи!

Редактировать: На самом деле, перечитав это, я думаю, что это просто поймает семьи, где нет видов в пределах рода. Вы можете добавить «и (g.id is null)» тоже, я думаю.

1

Sub-выберите на помощь ...


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, но вместо этого выбирает не более одной строки от genus и species.

+0

Разве мы с тобой разлучились при рождении, Квасной? – tpdi

6

Мета-ответ (комментарий на двух предыдущих ответов):

Использование IN имеет тенденцию деградировать к чему-то очень, как OR (дизъюнкция) всех членов в IN. Плохая работа.

Выполнение левого соединения и поиск нулевого значения - это улучшение, но это обскурантист. Если мы можем сказать, что мы имеем в виду, скажем, это в Вау, что это clossest, как мы бы сказали это на естественном языке:

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, по крайней мере, на каких-либо современных СУБД.

Кроме того, если семья имеет много родов (и в биологии, большинство семейств), мы собираемся получить один ряд (семья, род), когда все, о чем мы заботимся, это семья. Итак, давайте сделаем один ряд для каждой семьи:

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 плохо для производительности. Можете ли вы рассказать об этом или указать мне на ресурс, где я могу узнать больше об относительной производительности нескольких операций БД?

Подумайте об этом таким образом. Скажем, что в SQL не было оператора IN. Как вы подделали IN?

К серии ОШ:

where foo in (1, 2, 3) 

эквивалентно

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

Хорошо, скажете вы, но это еще не говорит мне, почему это плохо. Это плохо, потому что часто нет подходящего способа использовать ключ или индекс, чтобы посмотреть это. Таким образом, вы получаете либо а) сканирование таблицы, где для каждой дизъюнкции (или предиката или элемента списка IN) строка тестируется, пока тест не будет истинным или список не будет исчерпан. Или б) вы получите сканирование таблицы для каждого из этих дизъюнкций. Второй случай (б) может быть на самом деле лучше, поэтому вы иногда видите отборное с OR превратился в один выбор для каждого этапа ИЛИ 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 в соединение - и другие ответы, которые вам были предоставлены, - это именно те случаи, когда это наиболее вероятно.

Но если мы можем явно включить наш запрос в объединение, нам не нужно задаваться вопросом, является ли оптимизатор запросов умным. И в общем, объединения - это то, что лучше всего использует databse.

+0

Это было очень ясное объяснение. Тем не менее, мне любопытно, почему использование IN или disjunctions плохо для производительности. Можете ли вы рассказать об этом или указать мне на ресурс, где я могу узнать больше об относительной производительности нескольких операций БД? – Calvin

Смежные вопросы