Pregunta

Considere el oráculo emp mesa.Me gustaría que los empleados con el salario más alto department = 20 y job = clerk.Suponga también que no existe una columna "empno" y que la clave principal incluye varias columnas.Puedes hacer esto con:

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

Esto funciona, pero tengo que duplicar la prueba deptno = 20 y job = 'CLERK', lo cual me gustaría evitar.¿Existe una forma más elegante de escribir esto, tal vez usando un group by?Por cierto, si esto importa, estoy usando Oracle.

¿Fue útil?

Solución

Lo siguiente está ligeramente sobredimensionado, pero es un buen patrón 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
  )

También tenga en cuenta que esto funcionará en Oracle y Postgress (creo), pero no en MS SQL.Para algo similar en MS SQL ver pregunta Consulta SQL para obtener el último precio

Otros consejos

Si estuviera seguro de la base de datos de destino, elegiría la solución de Mark Nold, pero si alguna vez desea algo de SQL* independiente del dialecto, intente

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
)

*Creo que esto debería funcionar en todas partes, pero no tengo los entornos para probarlo.

En Oracle lo haría con una función analítica, por lo que solo consultarías la tabla emp una 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

Es más simple, más fácil de leer y más eficiente.

Si desea modificarlo para enumerar esta información para todos los departamentos, deberá usar la cláusula "PARTICIÓN POR" en 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

¡Genial!No sabía que se podía hacer una comparación de (x, y, z) con el resultado de una instrucción SELECT.Esto funciona muy bien con Oracle.

Como nota al margen para otros lectores, a la consulta anterior le falta un "=" después de "(deptno,job,sal)".Quizás el formateador Stack Overflow se lo comió (?).

De nuevo, gracias Marcos.

En Oracle también puedes usar la declaración EXISTS, que en algunos casos es más rápida.

Por ejemplo...Seleccione Nombre, Número de Cust Where Cust In (Seleccione CUST_ID de Big_table) e ingresado> Sysdate -1 sería lento.

Pero seleccione Nombre, número de Cust CDonde existe (seleccione cust_id de big_table DONDE cust_id=c.cust_id ) E ingresado> Sysdate -1 sería muy rápido con la indexación adecuada.También puedes usar esto con múltiples parámetros.

Hay muchas soluciones.También puede mantener el diseño de su consulta original simplemente agregando alias de tabla y uniéndose a los nombres de las columnas; aún así, solo tendría DEPTNO = 20 y JOB = 'CLERK' en la consulta una 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
    )

También cabe señalar que la palabra clave "TODOS" se puede utilizar para este tipo de consultas, lo que le permitiría eliminar la función "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 eso ayude y tenga sentido.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top