2013-02-10 2 views
3

Я делаю введение Stanfords в курс DB, и это одно из домашних заданий. Мой код делает работу хорошо, но мне не очень нравится, как я повторно тот же Select-FROM-РЕГИСТРИРУЙТЕСЬ часть дважды:Есть ли более элегантный способ написания этого SQL-запроса?

SELECT name, grade 
FROM Highschooler 
WHERE 
    ID IN (
     SELECT H1.ID 
     FROM Friend 
     JOIN Highschooler AS H1 
      ON Friend.ID1 = H1.ID 
     JOIN Highschooler AS H2 
      ON Friend.ID2 = H2.ID 
     WHERE H1.grade = H2.grade  
    ) AND 
    ID NOT IN (
     SELECT H1.ID 
     FROM Friend 
     JOIN Highschooler AS H1 
      ON Friend.ID1 = H1.ID 
     JOIN Highschooler AS H2 
      ON Friend.ID2 = H2.ID 
     WHERE H1.grade <> H2.grade 
    ) 
ORDER BY grade, name 

Это SQL схемы для двух таблиц, используемых в коде:

Highschooler(ID int, name text, grade int); 
Friend(ID1 int, ID2 int); 

Мне пришлось запросить у всех старшеклассников, у которых есть друзья только в одном классе, а не в других классах. Есть ли способ как-то написать код ниже один раз и повторно использовать его два раза для двух разных предложений WHERE = и <>?

SELECT H1.ID 
    FROM Friend 
    JOIN Highschooler AS H1 
     ON Friend.ID1 = H1.ID 
    JOIN Highschooler AS H2 
     ON Friend.ID2 = H2.ID 

EDIT: Мы должны обеспечить SQLite код.

ответ

0

Некоторые базы данных поддерживают ключевое слово минус.

select whatever 
from wherever 
where id in 
(select id 
from somewhere 
where something 
minus 
select id 
from somewhere 
where something else 
) 

Другие базы данных поддерживают ту же концепцию, но с ключевым словом, кроме, а не минусом.

4

Это «плакат ребенок» пример для WHERE EXISTS запроса:

SELECT name, grade 
FROM Highschooler ME 
WHERE EXISTS (
    SELECT 1 
    FROM Friend F 
    JOIN Highschooler OTHER on F.ID2=OTHER.ID 
    WHERE F.ID1=ME.ID AND OTHER.Grade = ME.GRADE 
) 
AND NOT EXISTS (
    SELECT 1 
    FROM Friend F 
    JOIN Highschooler OTHER on F.ID2=OTHER.ID 
    WHERE F.ID1=ME.ID AND OTHER.Grade <> ME.GRADE 
) 

EXISTS условие true, если его SELECT возвращает одну или несколько подряд; в противном случае это false. Все, что вам нужно сделать, это: соотнести внутренний подзапрос с внешним (часть F.ID1=ME.ID) и добавить оставшиеся ограничения, которые вам нужны (OTHER.Grade = ME.GRADE или OTHER.Grade <> ME.GRADE) к вашему запросу.

1

Иногда вы можете получить более естественную форму запроса при включении некоторых фильтрующих объединений в заданные операции, такие как UNION или MINUS/EXCEPT. Запрос ваш может быть, например, записать в виде (псевдо-код):

SELECT H.id 
    FROM Highschooler H 
    JOIN .... | has a friend 
    WHERE ... | in SAME grade 

EXCEPT 

    SELECT H.id 
    FROM Highschooler H 
    JOIN .... | has a friend 
    WHERE ... | in OTHER grade 

некоторые SQL двигатели используют ключевое слово «МИНУС», некоторые используют «кроме».

Но обратите внимание, что очень похоже на UNION, это выполнит оба запроса, а затем отфильтрует их результаты. Это может иметь разную производительность, чем один запрос «все-все-все», но помните, что не обязательно хуже. Много раз я нахожу, что он даже имеет лучшую производительность, поскольку «исключая» по одному столбцу, особенно отсортированному, очень быстро

Кроме того, если ваш механизм БД позволяет, вы можете попытаться использовать View или CTE, чтобы сократить исходный запрос , но я не вижу большого смысла в этом, кроме эстетики

4

Это типичный вопрос о группах, связанных с человеком. Когда вы сталкиваетесь с таким вопросом, один из подходов состоит в том, чтобы использовать объединения (глядя на вещи попарно). Часто лучший подход заключается в том, чтобы использовать агрегацию для одновременного просмотра всей группы.

Проницательность здесь заключается в том, что если у вас есть группа друзей и все находятся в одном классе, то минимальный и максимальный оценки будут одинаковыми.

Этого намека может быть достаточно для того, чтобы вы могли написать запрос. Если да, остановитесь здесь.

Запрос, который возвращает то, что вы хотите, намного проще, чем то, что вы делали. Вам просто нужно смотреть на нивелируют друг:

SELECT f.id1 
FROM Friend f jJOIN 
    Highschooler fh 
    ON Friend.ID1 = fh.ID join 
group by f.id1 
having max(fh.grade) = min(fh.grade) 

Предложения having гарантирует, что все такое же (игнорировать NULL значения).

EDIT:

Эта версия отвечает на вопрос: Какие highschoolers есть друзья все из которых находятся в том же классе. Ваш вопрос неоднозначен. Возможно, вы имеете в виду, что друзья и оригинальное лицо все в одном классе. Если да, то вы можете сделать это с небольшой модификацией. Один из способов изменить положение having на:

having max(fh.grade) = min(fh.grade) and 
     max(fh.grade) = (select grade from Highschooler h where f.id1 = h.id1) 

Это проверяет, что друзья и оригинальный человек, все в том же классе.

+0

+1 да, это, вероятно, лучшее предложение, поскольку оно правильно использует всю информацию, которую мы имеем о данных, которые хотели бы вернуть, и делает это на одном уровне запроса. – quetzalcoatl

+0

Прошу прощения, но я не понимаю, как я могу использовать/использовать ваш код. Я пробовал различные варианты, и никто из них не вернул правильные записи. – pootzko

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