删除除最近的记录以外的所有记录?

sim*_*sim 16 sql oracle

我有一个一对多关系的两个数据库表.数据如下所示:

select * from student, application
Run Code Online (Sandbox Code Playgroud)

结果集:

+-----------+---------------+---------------------+
| 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        |
+-----------+---------------+---------------------+
Run Code Online (Sandbox Code Playgroud)

我想删除除最新应用程序之外的所有应用程序.换句话说,每个学生必须只有一个应用程序链接到它.使用上面的示例,数据应如下所示:

+-----------+---------------+---------------------+
| StudentID | ApplicationID | ApplicationDateTime |
+-----------+---------------+---------------------+
| 1         | 20002         | 15 May 2011         |
| 2         | 20005         | 05 June 2011        |
+-----------+---------------+---------------------+
Run Code Online (Sandbox Code Playgroud)

我将如何构建我的DELETE语句来过滤掉正确的记录?

a_h*_*ame 20

DELETE FROM student
WHERE ApplicationDateTime <> (SELECT max(ApplicationDateTime) 
                              FROM student s2
                              WHERE s2.StudentID  = student.StudentID)
Run Code Online (Sandbox Code Playgroud)

鉴于评论中的长时间讨论,请注意以下内容:

上面的语句是正确的实现语句级读一致性,无论任何更改表语句运行时,任何数据库上运行.

我确定知道即使对表进行并发修改也能正常工作的数据库:Oracle(这个问题所针对的),Postgres,SAP HANA,Firebird(很可能是使用InnoDB的MySQL).因为它们都保证在语句开始时的数据的一致视图.更改<><不会改变任何内容(包括此问题的Oracle)

对于上述数据库,该声明是不是因为虚读或不可重复读只能之间发生受隔离级别不在一个-语句单一声明.

对于没有正确实现MVCC并依赖于锁定来管理并发(因此阻止并发写访问)的数据库,如果同时更新表,这实际上可能会产生错误的结果.对于那些<可能需要使用变通方法.


Ale*_*ole 6

您可以使用row_number()(或rank()或者dense_rank(),甚至只是rownum虚列),以命令适用于记录,然后用这个顺序来决定哪些丢弃.在这种情况下,order by 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
Run Code Online (Sandbox Code Playgroud)

然后,您可以删除排名高于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.
Run Code Online (Sandbox Code Playgroud)