Вопрос

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

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

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

Это было полезно?

Решение

Следующий пример несколько усложнен, но является хорошим шаблоном 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-запрос для получения последней цены

Другие советы

Если бы я был уверен в целевой базе данных, я бы выбрал решение Марка Нольда, но если вам когда-нибудь понадобится какой-нибудь диалектно-независимый 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
)

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

В 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

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

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

Еще раз спасибо, Марк.

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

Например...Выберите имя, номер из CUSH, где CUSH в (выберите cust_id из big_table) и введен> sysdate -1 будет медленным.

Но выберите имя, номер из CUST сГде существует (выберите cust_id из big_table ГДЕ cust_id=c.cust_id ) И введенный> sysdate -1 был бы очень быстрым с надлежащей индексацией.Вы также можете использовать это с несколькими параметрами.

Есть много решений.Вы также можете сохранить исходный макет запроса, просто добавив псевдонимы таблиц и объединив имена столбцов, но в запросе все равно будут только один раз 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
    )

Также можно отметить, что для этих типов запросов можно использовать ключевое слово «ВСЕ», что позволит вам удалить функцию «МАКС».

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
    )

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top