Aqui estão duas tabelas.
SCHOOL_STAFF
SCHOOL_CODE + STAFF_TYPE_NAME + LAST_UPDATE_DATE_TIME + PERSON_ID ================================================================= ABE Principal 24-JAN-13 111222 ABE Principal 09-FEB-12 222111
PESSOAS
PERSON_ID + NAME ================= 111222 ABC 222111 XYZ
Aqui está minha consulta ao oráculo.
SELECT MAX(LAST_UPDATE_DATE_TIME) AS LAST_UPDATE, SCHOOL_CODE, PERSON_ID FROM SCHOOL_STAFF WHERE STAFF_TYPE_NAME="Principal" GROUP BY SCHOOL_CODE, PERSON_ID ORDER BY SCHOOL_CODE;
o que dá este resultado
LAST_UPDATE SCHOOL_CODE PERSON_ID ===========+===========+========= 24-JAN-13 ABE 111222 09-FEB-12 ABE 222111
Quero selecionar o primeiro para a escola que tem a última data.
Obrigado.
Resposta
Sua consulta atual não está dando o resultado desejado porque você está usando um GROUP BY
cláusula na coluna PERSON_ID
que possui um valor único para ambas as entradas. Como resultado, você retornará ambas as linhas.
Existem algumas maneiras de resolver isso. Você pode usar uma subconsulta para aplicar a função de agregação para retornar o max(LAST_UPDATE_DATE_TIME)
para cada SCHOOL_CODE
:
select s1.LAST_UPDATE_DATE_TIME, s1.SCHOOL_CODE, s1.PERSON_ID from SCHOOL_STAFF s1 inner join ( select max(LAST_UPDATE_DATE_TIME) LAST_UPDATE_DATE_TIME, SCHOOL_CODE from SCHOOL_STAFF group by SCHOOL_CODE ) s2 on s1.SCHOOL_CODE = s2.SCHOOL_CODE and s1.LAST_UPDATE_DATE_TIME = s2.LAST_UPDATE_DATE_TIME;
Veja SQL Fiddle com Demo
Ou você pode usar um função de janela para retornar as linhas de dados para cada escola com o mais recente LAST_UPDATE_DATE_TIME
:
select SCHOOL_CODE, PERSON_ID, LAST_UPDATE_DATE_TIME from ( select SCHOOL_CODE, PERSON_ID, LAST_UPDATE_DATE_TIME, row_number() over(partition by SCHOOL_CODE order by LAST_UPDATE_DATE_TIME desc) seq from SCHOOL_STAFF where STAFF_TYPE_NAME="Principal" ) d where seq = 1;
Veja SQL Fiddle com Demo
Esta consulta implementa row_number()
que atribui um número único a cada linha na partição de SCHOOL_CODE
e colocado em ordem decrescente com base no LAST_UPDATE_DATE_TIME
.
Como observação, a função JOIN com agregação não é exatamente igual à versão row_number()
. Se você tiver duas linhas com o mesmo tempo de evento, o JOIN retornará ambas as linhas, enquanto o row_number()
retornará apenas uma. Se você deseja retornar ambos com uma função de janelamento, considere usar a função de janelamento, pois ela retornará empates:
select SCHOOL_CODE, PERSON_ID, LAST_UPDATE_DATE_TIME from ( select SCHOOL_CODE, PERSON_ID, LAST_UPDATE_DATE_TIME, rank() over(partition by SCHOOL_CODE order by LAST_UPDATE_DATE_TIME desc) seq from SCHOOL_STAFF where STAFF_TYPE_NAME="Principal" ) d where seq = 1;
Veja a Demo
Comentários
- Obrigado, acho a junção interna a uma tabela de subconsulta (exemplo 1 acima) para ser a mais intuitiva .. e não ' não exige que eu aprenda qual partição por é tudo cerca de. Aqui está uma olhada em uma sintaxe semelhante ao exemplo 1: selecione oT.dateField, oT.siteID, oT.field1, oT.field2, oT.field3, de originalTable como oT junção interna (selecione max (dateField) como newestDate, siteID do grupo originalTable por siteID) como newTable em oT.siteID = newTable.site_ID e oT.dateField = newTable.newestDate ordem por oT.siteID asc Para mim, isso explica melhor o que ' s acontecendo na subconsulta.
Resposta
I “Estou surpreso que ninguém tenha aproveitado as funções da janela além de row_number ()
Aqui estão alguns dados para brincar:
CREATE TABLE SCHOOL_STAFF ( LAST_UPDATE_DATE_TIME VARCHAR(20), SCHOOL_CODE VARCHAR(20), PERSON_ID VARCHAR(20), STAFF_TYPE_NAME VARCHAR(20) ); INSERT INTO SCHOOL_STAFF VALUES ("24-JAN-13", "ABE", "111222", "Principal"); INSERT INTO SCHOOL_STAFF VALUES ("09-FEB-12", "ABE", "222111", "Principal");
O A cláusula OVER () cria uma janela para a qual você definirá seus grupos agregados. Neste caso, estou particionando apenas no SHOOL_CODE, então veremos o FIRST_VALUE, que virá de LAST_UPDATE_DATE_TIME, agrupado por SCHOOL_CODE, e na ordem de LAST_UPDATE_DATE_TIME em ordem decrescente. Este valor será aplicado a toda a coluna para cada SCHOOL_CODE.
É importante prestar atenção ao particionamento e ordenação na cláusula over ().
SELECT DISTINCT FIRST_VALUE(LAST_UPDATE_DATE_TIME) OVER (PARTITION BY SCHOOL_CODE ORDER BY LAST_UPDATE_DATE_TIME DESC) AS LAST_UPDATE ,FIRST_VALUE(SCHOOL_CODE) OVER (PARTITION BY SCHOOL_CODE ORDER BY LAST_UPDATE_DATE_TIME DESC) AS SCHOOL_CODE ,FIRST_VALUE(PERSON_ID) OVER (PARTITION BY SCHOOL_CODE ORDER BY LAST_UPDATE_DATE_TIME DESC) AS PERSON_ID FROM SCHOOL_STAFF WHERE STAFF_TYPE_NAME = "Principal" ORDER BY SCHOOL_CODE
Retorna:
24-JAN-13 ABE 111222
Isso deve eliminar a necessidade de GROUP BY e subconsultas em geral. No entanto, você deve incluir DISTINCT.
Comentários
- Isso é bom, mas há uma maneira de evitar a repetição da cláusula over para todas as colunas?
Resposta
select LAST_UPDATE_DATE_TIME as LAST_UPDATE, SCHOOL_CODE, PERSON_ID from SCHOOL_STAFF WHERE STAFF_TYPE_NAME="Principal" AND LAST_UPDATE_DATE_TIME = (SELECT MAX(LAST_UPDATE_DATE_TIME) FROM SCHOOL_STAFF s2 WHERE PERSON_ID = s2.PERSON_ID)
Comentários
- Em vez de postar apenas código, você deve tentar explicar como isso responde à pergunta; e potencialmente o que o OP estava fazendo incorretamente.