У меня есть две таблицы БД в отношении "один ко многим". Данные выглядят так:

select * from student, application

Resultset :

+-----------+---------------+---------------------+
| StudentID | ApplicationID | ApplicationDateTime |
+-----------+---------------+---------------------+
| 1         | 20001         | 12 April 2011       |
| 1         | 20002         | 15 May 2011         |
| 2         | 20003         | 02 Feb 2011         |
| 2         | 20004         | 13 March 2011       |
| 2         | 20005         | 05 June 2011        |
+-----------+---------------+---------------------+

Я хочу удалить все приложения, кроме самого последнего. Другими словами, с каждым студентом должно быть связано только одно приложение. В приведенном выше примере данные должны выглядеть так:

+-----------+---------------+---------------------+
| StudentID | ApplicationID | ApplicationDateTime |
+-----------+---------------+---------------------+
| 1         | 20002         | 15 May 2011         |
| 2         | 20005         | 05 June 2011        |
+-----------+---------------+---------------------+

Как мне построить оператор DELETE, чтобы отфильтровать правильные записи?

22
sim 30 Авг 2011 в 09:18

3 ответа

Лучший ответ
DELETE FROM student
WHERE ApplicationDateTime <> (SELECT max(ApplicationDateTime) 
                              FROM student s2
                              WHERE s2.StudentID  = student.StudentID)

Учитывая долгое обсуждение в комментариях, обратите внимание на следующее:

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

Базы данных, в которых я точно знаю, что это работает правильно даже с одновременными модификациями таблицы: Oracle (тот, о котором идет речь в этом вопросе), Postgres, SAP HANA, Firebird (и, скорее всего, MySQL, использующий InnoDB). Потому что все они гарантируют непротиворечивое представление данных в момент начала выполнения оператора. Изменение <> на < ничего не изменит для них (включая Oracle, о котором идет речь в этом вопросе)

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

Для базы данных, которая не реализует должным образом MVCC и полагается на блокировку для управления параллелизмом (таким образом, блокируя одновременный доступ на запись), это может фактически привести к неверным результатам, если таблица обновляется одновременно. Для них, вероятно, потребуется обходной путь с использованием <.

23
a_horse_with_no_name 21 Ноя 2016 в 15:16

Вы можете использовать row_number() (или rank() или dense_rank(), или даже просто псевдоколонку rownum), чтобы применить порядок к записям, а затем использовать этот порядок, чтобы решить, какой отказаться. В этом случае сортировка по applicationdatetime desc дает приложению с самой последней датой для каждого студента рейтинг 1:

select studentid, applicationid from (
    select studentid, applicationid,
        row_number() over (partition by studentid
            order by applicationdatetime desc) as rn
    from application
)
where rn = 1;

 STUDENTID APPLICATIONID
---------- -------------
         1         20002
         2         20005

Затем вы можете удалить все, что имеет рейтинг выше 1, что сохранит нужные вам записи:

delete from application
where (studentid, applicationid) in (
    select studentid, applicationid from (
        select studentid, applicationid,
            row_number() over (partition by studentid
                order by applicationdatetime desc) as rn
        from application
    )
    where rn > 1
);

3 rows deleted.
6
Alex Poole 30 Авг 2011 в 11:51

Сначала вы можете это сделать

DELETE FROM [student]
           or [application]
WHERE (studentid, applicationid) NOT IN (SELECT StudentID
                                               ,MAX(ApplicationID)
                                         FROM student
                                             ,application
group by StudentID);

Но есть другое решение, вы можете создать резервную копию таблицы после удаления всех записей в ваших таблицах и после вставки ваших данных (что вы хотите) с максимальными значениями, выбранными в ваших таблицах.

1
Juan Carlos Oropeza 7 Июн 2016 в 20:22