Selecione qual tem data máxima ou data mais recente

15

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 do oracle.

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á esse resultado

LAST_UPDATE SCHOOL_CODE PERSON_ID
===========+===========+=========
24-JAN-13   ABE         111222
09-FEB-12   ABE         222111

Quero selecionar o primeiro da escola que tiver data mais recente.

Obrigado.

riz
fonte

Respostas:

28

Sua consulta atual não está fornecendo o resultado desejado porque você está usando uma GROUP BYcláusula na PERSON_IDcoluna que possui um valor exclusivo para as duas entradas. Como resultado, você retornará as duas linhas.

Existem algumas maneiras de resolver isso. Você pode usar uma subconsulta para aplicar a função agregada para retornar o valor 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 demonstração

Ou você pode usar uma função de janelas para retornar as linhas de dados de cada escola com as mais recentes 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 demonstração

Esta consulta implementa o row_number()que atribui um número único a cada linha na partição SCHOOL_CODEe é colocada em uma ordem decrescente com base no LAST_UPDATE_DATE_TIME.

Como uma observação lateral, o JOIN com função agregada não é exatamente o mesmo que a row_number()versão. Se você tiver duas linhas com o mesmo horário do evento, JOIN retornará as duas linhas, enquanto row_number()retornará apenas uma. Se você deseja retornar ambos com uma função de janelas, considere usar a rank()função de janelas, pois ela retornará laços:

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;

Ver demonstração

Taryn
fonte
4

Estou surpreso que ninguém tenha aproveitado as funções da janela além do row_number ()

Aqui estão alguns dados para jogar:

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');

A cláusula OVER () cria uma janela para a qual você definirá seus grupos agregados. Nesse caso, estou particionando apenas no SHOOL_CODE, portanto veremos o FIRST_VALUE, que virá de LAST_UPDATE_DATE_TIME, agrupado por SCHOOL_CODE e na ordem de LAST_UPDATE_DATE_TIME por ordem decrescente. Este valor será aplicado a toda a coluna para cada SCHOOL_CODE.

É importante prestar muita atenção ao seu particionamento e pedido 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

Devoluções:

24-JAN-13   ABE 111222

Isso deve eliminar a necessidade de GROUP BY e subconsultas na maior parte. Você deve ter certeza de incluir o DISTINCT.

Andrew
fonte
1
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)
MouseInfa
fonte
1
Em vez de postar apenas o código, você deve tentar explicar como isso responde à pergunta; e potencialmente o que o OP estava fazendo incorretamente.
Max Vernon