SQL:función agregada y agrupada por
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.
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.