2013-04-28 2 views
3

Мой учитель базы данных попросил меня написать (на сервере Oracle) запрос: выберите идентификатор_группы с наибольшим количеством очков в среднем за год 2010Oracle ORDER BY с ROWNUM или HAVING> = ALL

я писал:

SELECT * FROM (
    SELECT groupid, AVG(score) average FROM points 
    WHERE yr = 2010 
    AND score IS NOT NULL 
    GROUP BY groupid 
    ORDER BY average DESC 
) WHERE rownum = 1; 

Мой учитель говорит мне, что этот запрос «лучше»:

SELECT groupid, AVG(score) average FROM points 
WHERE yr = 2010 
GROUP BY groupid 
HAVING AVG(score) >= ALL (
    SELECT AVG(score) FROM points 
    WHERE yr = 2010 
    GROUP BY groupid 
); 

Какой самый быстрый/лучше? Есть ли еще лучшее решение (только для Oracle)? Спасибо.

ответ

3

Есть две причины, по которым ваш инструктор говорит вам об этом.

  1. Модель данных. Реляционные СУБД имеют дело с наборами, а не с списками. Если вы изучаете SQL, вам лучше думать в терминах наборов кортежей, которые неупорядочены, чем в списках заказов. Вам лучше понять, как запросить СУБД. Я считаю ваше решение взломанным: это работает, частично, поскольку, как указал Perun_x, он не работает, если более чем один кортеж соответствует результату. Это противоречит модели данных и духу SQL).

  2. Переносимость. Это настоящий убийца. Ваш код будет работать на Oracle, но не в других СУБД, которые не поддерживают атрибут row_number (каждый имеет свой собственный способ сделать это).

--dmg

+0

Спасибо. В самом деле, у меня нет «набора кортежей». Но образовательный путь рядом с режимом исполнения не для меня, действительно интересный.Для точки переносимости это абсолютно верно. Я бы предпочел использовать LIMIT внутри rownum, который тоже не переносится, я знаю. С другой стороны, мы также изучаем специальный код Oracle ... – Maxux

2

Запросы не эквивалентны. Первый запрос всегда выбирает 1 строку. Второй выбирает все строки с самым высоким средним (теоретически может быть больше таких строк).

+0

Действительно. В нашем случае это не должно происходить. – Maxux

2

Я предпочитаю вашу версию, считая, что одной строки достаточно для того, что вам нужно. Моя проблема с версией учителя в первую очередь читабельна. Мне трудно разобрать.

Ваша версия, по сути, говорит: «Закажите группы по их среднему значению и возьмите тот, у которого самый высокий средний». Версия для учителей по существу говорит: «Найдите среднее значение, которое больше или равно любому из средних значений групп». Это может быть субъективным, но я считаю первое более понятным, чем последнее.

Что касается того, что быстрее. Для достижения максимальной ценности вам необходимо выполнить агрегацию и сортировку. Вторая версия должна выполнять две агрегации и объединение. Я бы подумал, что метод агрегирования/сортировки будет быстрее, но единственный способ узнать это - проверить производительность конкретной конфигурации системы и набора данных.

Альтернативная формулировка, которая должна быть примерно эквивалентна ваша с точки зрения производительности, является:

select groupid, avgscore 
from (select groupid, avg(score) as avgscore, 
      row_number() over (order by avg(score) desc) as seqnum 
    from points 
    where yr = 2010 
    group by groupid 
    ) t 
where seqnum = 1 

Преимущество здесь в том, что вы можете изменить row_number() к dense_rank(), чтобы получить либо один из лучших строк или все лучшие строки.

+0

Спасибо. Я попробовал какой-то метод (тоже попробовал инструкцию WITH), и да, метод HAVING значительно медленнее. Ваша альтернативная формулировка интересна. – Maxux

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

  • Нет связанных вопросов^_^