Wybierz, która data jest maksymalna lub ostatnia

15

Oto dwie tabele.

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

OSOBY

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

Oto moje zapytanie 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;

co daje te wyniki

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

Chcę wybrać pierwszą dla szkoły, która ma najnowszą datę.

Dzięki.

riz
źródło

Odpowiedzi:

28

Twoje obecne zapytanie nie daje pożądanego rezultatu, ponieważ używasz GROUP BYklauzuli w PERSON_IDkolumnie, która ma unikalną wartość dla obu wpisów. W rezultacie zwrócisz oba wiersze.

Istnieje kilka sposobów rozwiązania tego problemu. Za pomocą podzapytania można zastosować funkcję agregującą, aby zwrócić max(LAST_UPDATE_DATE_TIME)dla każdego 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;

Zobacz SQL Fiddle with Demo

Możesz też użyć funkcji okienkowania, aby zwrócić wiersze danych dla każdej szkoły z najnowszymi 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;

Zobacz SQL Fiddle with Demo

To zapytanie implementuje, row_number()który przypisuje unikalny numer do każdego wiersza w partycji SCHOOL_CODEi umieszczony w kolejności malejącej na podstawie LAST_UPDATE_DATE_TIME.

Na marginesie, JOIN z funkcją agregującą nie jest dokładnie taki sam jak row_number()wersja. Jeśli masz dwa wiersze z tym samym czasem zdarzenia, JOIN zwróci oba wiersze, a row_number()tylko jeden zwróci. Jeśli chcesz zwrócić oba z funkcją okienkowania, rozważ użycie rank()funkcji okienkowania, ponieważ zwróci ona więzi:

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;

Zobacz demo

Taryn
źródło
4

Dziwię się, że nikt nie skorzystał z funkcji okna poza row_number ()

Oto niektóre dane do zabawy:

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

Klauzula OVER () tworzy okno, w którym zdefiniujesz grupy agregujące. W takim przypadku partycjonuję tylko na SHOOL_CODE, więc zobaczymy FIRST_VALUE, który będzie pochodził z LAST_UPDATE_DATE_TIME, pogrupowane według SCHOOL_CODE, i w kolejności LAST_UPDATE_DATE_TIME w porządku malejącym. Ta wartość zostanie zastosowana do całej kolumny dla każdego KODU SZKOŁY.

Ważne jest, aby zwrócić szczególną uwagę na partycjonowanie i porządkowanie w klauzuli 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

Zwroty:

24-JAN-13   ABE 111222

To powinno w większości wyeliminować potrzebę GROUP BY i Subqueries. Będziesz jednak musiał upewnić się, że podałeś DISTINCT.

Andrzej
źródło
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
źródło
1
Zamiast zamieszczać tylko kod, powinieneś spróbować wyjaśnić, jak to odpowiada na pytanie; i potencjalnie to, co PO robił niepoprawnie.
Max Vernon