Sélectionnez la date maximale ou la dernière date


15

Voici deux tableaux.

PERSONNEL DE L'ÉCOLE

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

PERSONNES

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

Voici ma requête 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;

ce qui donne ces résultats

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

Je veux sélectionner le premier pour l'école qui a la dernière date.

Merci.

Réponses:


28

Votre requête actuelle ne donne pas le résultat souhaité car vous utilisez une GROUP BYclause sur la PERSON_IDcolonne qui a une valeur unique pour les deux entrées. Par conséquent, vous retournerez les deux lignes.

Il existe plusieurs façons de résoudre ce problème. Vous pouvez utiliser une sous-requête pour appliquer la fonction d'agrégation pour renvoyer le max(LAST_UPDATE_DATE_TIME)pour chacun 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;

Voir SQL Fiddle avec démo

Ou vous pouvez utiliser une fonction de fenêtrage pour renvoyer les lignes de données pour chaque école avec la plus récente 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;

Voir SQL Fiddle avec démo

Cette requête implémente row_number()qui attribue un numéro unique à chaque ligne de la partition de SCHOOL_CODEet placé dans un ordre décroissant basé sur le LAST_UPDATE_DATE_TIME.

En remarque, la fonction JOIN avec agrégat n'est pas exactement la même que la row_number()version. Si vous avez deux lignes avec le même temps d'événement, JOIN renvoie les deux lignes, tandis que row_number()n'en renvoie qu'une. Si vous souhaitez renvoyer les deux avec une fonction de fenêtrage, envisagez d'utiliser la rank()fonction de fenêtrage à la place car elle renverra des liens:

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;

Voir la démo


4

Je suis surpris que personne n'ait profité des fonctions de la fenêtre au-delà de row_number ()

Voici quelques données avec lesquelles jouer:

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 clause OVER () crée une fenêtre pour laquelle vous définirez vos groupes d'agrégats. Dans ce cas, je ne partitionne que sur le SHOOL_CODE, nous verrons donc le FIRST_VALUE, qui proviendra de LAST_UPDATE_DATE_TIME, groupé par SCHOOL_CODE, et dans l'ordre de LAST_UPDATE_DATE_TIME par ordre décroissant. Cette valeur sera appliquée à la colonne entière pour chaque SCHOOL_CODE.

Il est important de porter une attention particulière à votre partitionnement et à votre classement dans la clause 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

Retour:

24-JAN-13   ABE 111222

Cela devrait éliminer votre besoin de GROUP BY et de sous-requêtes pour la plupart. Vous devrez vous assurer d'inclure DISTINCT cependant.


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
Au lieu de publier uniquement du code, vous devriez essayer d'expliquer comment cela répond à la question; et potentiellement ce que le PO faisait incorrectement.
Max Vernon
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.