我有两个一对多关系的数据库表。数据如下所示:
select * from student, application
结果集:
+-----------+---------------+---------------------+
| 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语句来过滤掉正确的记录?
答案 0 :(得分:19)
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并依赖于锁定来管理并发性(从而阻止并发写访问)的数据库,如果同时更新表,这实际上可能会产生错误的结果。对于那些可能需要使用<
的解决方法。
答案 1 :(得分:5)
您可以使用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.
答案 2 :(得分:1)
首先你可以这样做
DELETE FROM [student]
or [application]
WHERE (studentid, applicationid) NOT IN (SELECT StudentID
,MAX(ApplicationID)
FROM student
,application
group by StudentID);
但是还有另一个解决方案,您可以在删除表格中的所有记录之后以及在表格中选择最大值数据后插入数据(您想要的内容)之后创建备份表。