2008-09-09 5 views
3

Рассмотрим таблицу Oracle emp. Я хотел бы получить сотрудников с высшей зарплатой с department = 20 и job = clerk. Также предположим, что нет столбца «empno», а первичный ключ содержит несколько столбцов. Вы можете сделать это с помощью:SQL: агрегатная функция и группа по

select * from scott.emp 
where deptno = 20 and job = 'CLERK' 
and sal = (select max(sal) from scott.emp 
      where deptno = 20 and job = 'CLERK') 

Это работает, но я должен дублировать тест DEPTNO = 20 и задания = 'клерка, который я хотел бы избежать. Есть ли более элегантный способ написать это, возможно, используя group by? Кстати, если это имеет значение, я использую Oracle.

ответ

3

Ниже приведена небольшая надстройка, но это хороший шаблон SQL для запросов «top x».

SELECT 
* 
FROM 
scott.emp 
WHERE 
(deptno,job,sal) IN 
(SELECT 
    deptno, 
    job, 
    max(sal) 
    FROM 
    scott.emp 
    WHERE 
    deptno = 20 
    and job = 'CLERK' 
    GROUP BY 
    deptno, 
    job 
) 

Также обратите внимание, что это будет работать в Oracle и Postgress (я думаю), но не в MS SQL. Для чего-то подобного в MS SQL см. Вопрос SQL Query to get latest price

0

Это замечательно! Я не знал, что вы могли бы выполнить сравнение (x, y, z) с результатом инструкции SELECT. Это отлично работает с Oracle.

В качестве примечания для других читателей, в приведенном выше запросе отсутствует «=» после »(deptno, job, sal)». Может быть, форматир Stack Overflow съел его (?).

Снова благодарим Марка.

+0

Нет проблем, я отредактировал ответ :) это должно быть IN – 2008-09-09 03:25:49

2

Если бы я был уверен в целевой базе данных, я бы с раствором Марка Nold, но если вы хотите какой-то диалект агностик SQL *, попробуйте

SELECT * 
FROM scott.emp e 
WHERE e.deptno = 20 
AND e.job = 'CLERK' 
AND e.sal = (
    SELECT MAX(e2.sal) 
    FROM scott.emp e2 
    WHERE e.deptno = e2.deptno 
    AND e.job = e2.job 
) 

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

0

В Oracle вы также можете использовать инструкцию EXISTS, которая в некоторых случаях выполняется быстрее.

Например ... ВЫБРАТЬ имя, номер ИЗ П ГДЕ Касты В (ВЫБОР cust_id ОТ big_table) И вошли> SYSDATE -1 будут медленными.

но SELECT, имя, номер ИЗ П с , где существует (SELECT cust_id ОТ big_table ГДЕ cust_id = c.cust_id) И вошло> SYSDATE -1 будут очень быстро с правильной индексацией. Вы также можете использовать это с несколькими параметрами.

0

Существует множество решений. Вы также можете сохранить исходный макет запроса, просто добавив псевдонимы таблиц и присоединившись к именам столбцов, вы все равно должны иметь только DEPTNO = 20 и JOB = 'CLERK' в запросе.

SELECT 
    * 
FROM 
    scott.emp emptbl 
WHERE 
    emptbl.DEPTNO = 20 
    AND emptbl.JOB = 'CLERK' 
    AND emptbl.SAL = 
    (
     select 
     max(salmax.SAL) 
     from 
     scott.emp salmax 
     where 
     salmax.DEPTNO = emptbl.DEPTNO 
     AND salmax.JOB = emptbl.JOB 
    ) 

Также можно отметить, что ключевое слово «ALL» может быть использован для этих типов запросов, которые позволили бы вам удалить функцию «MAX».

SELECT 
    * 
FROM 
    scott.emp emptbl 
WHERE 
    emptbl.DEPTNO = 20 
    AND emptbl.JOB = 'CLERK' 
    AND emptbl.SAL >= ALL 
    (
     select 
     salmax.SAL 
     from 
     scott.emp salmax 
     where 
     salmax.DEPTNO = emptbl.DEPTNO 
     AND salmax.JOB = emptbl.JOB 
    ) 

Я надеюсь, что это поможет и имеет смысл.

1

В Oracle я хотел бы сделать это с аналитической функцией, так что вы бы только запрос к таблице EMP раз:

SELECT * 
    FROM (SELECT e.*, MAX (sal) OVER() AS max_sal 
      FROM scott.emp e 
     WHERE deptno = 20 
      AND job = 'CLERK') 
WHERE sal = max_sal 

Это проще, легче читать и более эффективным.

Если вы хотите изменить его в список списка этой информации для всех отделов, то вам нужно использовать «PARTITION BY» в пункте OVER:

SELECT * 
    FROM (SELECT e.*, MAX (sal) OVER (PARTITION BY deptno) AS max_sal 
      FROM scott.emp e 
     WHERE job = 'CLERK') 
WHERE sal = max_sal 
ORDER BY deptno 
Смежные вопросы