Seleziona quale ha la data massima o la data più recente


15

Ecco due tabelle.

PERSONALE SCOLASTICO

SCHOOL_CODE + STAFF_TYPE_NAME + LAST_UPDATE_DATE_TIME + PERSON_ID
=================================================================
ABE           Principal         24-JAN-13               111222
ABE           Principal         09-FEB-12               222111

PERSONE

PERSON_ID + NAME
=================
111222      ABC
222111      XYZ

Ecco la mia domanda sull'oracolo.

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;

che dà questi risultati

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

Voglio selezionare il primo per la scuola che ha l'ultima data.

Grazie.

Risposte:


28

La tua query corrente non sta dando il risultato desiderato perché stai usando una GROUP BYclausola sulla PERSON_IDcolonna che ha un valore univoco per entrambe le voci. Di conseguenza, verranno restituite entrambe le righe.

Ci sono alcuni modi per risolverlo. È possibile utilizzare una sottoquery per applicare la funzione aggregata per restituire il max(LAST_UPDATE_DATE_TIME)per ciascuno 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;

Vedi SQL Fiddle with Demo

Oppure puoi usare una funzione di windowing per restituire le file di dati per ogni scuola con le più recenti 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;

Vedi SQL Fiddle with Demo

Questa query implementa row_number()che assegna un numero univoco a ciascuna riga nella partizione SCHOOL_CODEe posizionata in un ordine decrescente in base a LAST_UPDATE_DATE_TIME.

Come nota a margine, la JOIN con funzione aggregata non è esattamente la stessa della row_number()versione. Se hai due righe con lo stesso tempo dell'evento, JOIN restituirà entrambe le righe, mentre row_number()ne restituirà solo una. Se si desidera restituire entrambi con una funzione di windowing, prendere in considerazione l'utilizzo della rank()funzione di windowing in quanto restituirà legami:

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;

Vedi la demo


4

Sono sorpreso che nessuno abbia sfruttato le funzioni della finestra oltre row_number ()

Ecco alcuni dati con cui giocare:

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

La clausola OVER () crea una finestra per la quale si definiranno i gruppi aggregati. In questo caso, sto partizionando solo su SHOOL_CODE, quindi vedremo il FIRST_VALUE, che verrà da LAST_UPDATE_DATE_TIME, raggruppato da SCHOOL_CODE e nell'ordine di LAST_UPDATE_DATE_TIME in ordine decrescente. Questo valore verrà applicato all'intera colonna per ogni SCHOOL_CODE.

È importante prestare molta attenzione al partizionamento e all'ordinamento nella clausola 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

Ritorna:

24-JAN-13   ABE 111222

Ciò dovrebbe eliminare per la maggior parte la necessità di GROUP BY e subquery. Tuttavia, ti consigliamo di includere DISTINCT.


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)

1
Invece di pubblicare solo codice, dovresti provare a spiegare come questo risponde alla domanda; e potenzialmente ciò che l'OP stava facendo in modo errato.
Max Vernon,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.