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.

riz
la source

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

Taryn
la source
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.

Andrew
la source
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
la source
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