Selecione qual tem data máxima ou data mais recente

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.

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *