Выберите максимальную или последнюю дату

15

Вот две таблицы.

ШКОЛЬНЫЕ ВЕЩИ

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

ЛИЦА

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

Вот мой запрос оракула.

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;

который дает этот результат

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

Я хочу выбрать первый для школы, которая имеет самую последнюю дату.

Благодарю.

Риз
источник

Ответы:

28

Ваш текущий запрос не дает желаемого результата, потому что вы используете GROUP BYпредложение для PERSON_IDстолбца, которое имеет уникальное значение для обеих записей. В результате вы вернете обе строки.

Есть несколько способов, которые вы можете решить это. Вы можете использовать подзапрос, чтобы применить агрегатную функцию, возвращающую max(LAST_UPDATE_DATE_TIME)для каждого 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;

Смотрите SQL Fiddle с демо

Или вы можете использовать оконную функцию для возврата строк данных для каждой школы с самыми последними 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;

Смотрите SQL Fiddle с демо

Этот запрос реализует, row_number()который присваивает уникальный номер каждой строке в разделе SCHOOL_CODEи размещается в порядке убывания на основе LAST_UPDATE_DATE_TIME.

В качестве примечания, JOIN с агрегатной функцией не совсем совпадает с row_number()версией. Если у вас есть две строки с одним и тем же временем события, JOIN вернет обе строки, а row_number()только одну. Если вы хотите вернуть оба с помощью функции управления окнами, рассмотрите возможность использования rank()функции управления окнами, поскольку она будет возвращать связи:

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;

Посмотреть демо

Тарын
источник
4

Я удивлен, что никто не воспользовался оконными функциями, кроме row_number ()

Вот некоторые данные для игры:

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

Предложение OVER () создает окно, для которого вы будете определять группы агрегатов. В этом случае я делаю только разделы на SHOOL_CODE, поэтому мы увидим FIRST_VALUE, который будет происходить из LAST_UPDATE_DATE_TIME, сгруппированный по SCHOOL_CODE, и в порядке LAST_UPDATE_DATE_TIME по убыванию. Это значение будет применено ко всему столбцу для каждого SCHOOL_CODE.

Важно обратить пристальное внимание на разделение и порядок в предложении 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

Возвращает:

24-JAN-13   ABE 111222

Это должно устранить вашу потребность в GROUP BY и подзапросах по большей части. Вы обязательно должны включить 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)
MouseInfa
источник
1
Вместо того, чтобы публиковать только код, вы должны попытаться объяснить, как это отвечает на вопрос; и потенциально то, что ФП делал неправильно.
Макс Вернон,