Pergunta

Considere o Oráculo emp mesa.Eu gostaria de conseguir os funcionários com o salário mais alto com department = 20 e job = clerk.Suponha também que não existe uma coluna "empno" e que a chave primária envolve várias colunas.Você pode fazer isso com:

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

Isso funciona, mas tenho que duplicar o teste deptno = 20 e job = 'CLERK', o que gostaria de evitar.Existe uma maneira mais elegante de escrever isso, talvez usando um group by?Aliás, se isso importa, estou usando o Oracle.

Foi útil?

Solução

O seguinte é um pouco exagerado, mas é um bom padrão SQL para consultas "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
  )

Observe também que isso funcionará no Oracle e no Postgress (eu acho), mas não no MS SQL.Para algo semelhante no MS SQL, consulte a pergunta Consulta SQL para obter o preço mais recente

Outras dicas

Se eu tivesse certeza do banco de dados de destino, escolheria a solução de Mark Nold, mas se você quiser algum SQL * agnóstico de dialeto, tente

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
)

*Acredito que isso deveria funcionar em qualquer lugar, mas não tenho ambientes para testá-lo.

No Oracle eu faria isso com uma função analítica, então você só consultaria a tabela emp uma vez:

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

É mais simples, mais fácil de ler e mais eficiente.

Se quiser modificá-lo para listar essas informações para todos os departamentos, você precisará usar a cláusula "PARTITION BY" em 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

Isso é ótimo!Eu não sabia que você poderia fazer uma comparação de (x, y, z) com o resultado de uma instrução SELECT.Isso funciona muito bem com o Oracle.

Como observação para outros leitores, falta um "=" depois de "(deptno,job,sal)" na consulta acima.Talvez o formatador Stack Overflow tenha comido (?).

Mais uma vez, obrigado Marcos.

No Oracle você também pode usar a instrução EXISTS, que em alguns casos é mais rápida.

Por exemplo...Selecione o nome, número de custos onde custa (selecione cust_id de big_table) e digitado> sysdate -1 seria lento.

Mas selecione o nome, número de custos cOnde existe (selecione cust_id de big_table ONDE cust_id=c.cust_id ) E inserido> Sysdate -1 seria muito rápido com a indexação adequada.Você também pode usar isso com vários parâmetros.

Existem muitas soluções.Você também pode manter o layout de consulta original simplesmente adicionando aliases de tabela e juntando os nomes das colunas; você ainda teria DEPTNO = 20 e JOB = 'CLERK' na consulta uma vez.

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
    )

Também se pode notar que a palavra-chave "ALL" pode ser usada para estes tipos de consultas, o que permitiria remover a função "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
    )

Espero que ajude e faça sentido.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top