我可以在 Oracle 中删除没有子选择的最新记录吗?
Posted
技术标签:
【中文标题】我可以在 Oracle 中删除没有子选择的最新记录吗?【英文标题】:Can I delete the most recent record without sub-select in Oracle? 【发布时间】:2011-06-24 13:31:01 【问题描述】:我想要一条 SQL 语句来删除表中的最新记录。这是我的想法:
delete from daily_statistics
where process_date = (
select max(process_date)
from daily_statistics
);
但似乎有一种方法可以在没有子选择的情况下执行此操作,这可能效率低下。 (在我的情况下,效率实际上并不重要,我只想知道最简单、最易读的编码方式。)
【问题讨论】:
如果您每天运行此程序,会不会导致在删除之前只有今天的记录?那么“从每日统计中删除”会删除完全相同数量的记录吗? 我目前正在做一些特别的事情,手动过程会创建一个要删除的记录,总是只有一个。 你觉得Mr.FarmBoy会遇到什么低效问题? 对于这个特定的目的,您是否可以回滚插入记录的事务,或者执行 FLASHBACK TABLE 将其恢复到之前的状态?根据具体情况,这些可能是完成您需要的更快的方法。 @BurnAfterReading 注意我提到效率并不是我真正关心的问题。 【参考方案1】:最易读的方式可能是你写的。但根据各种因素,它可能会非常浪费。特别是,如果 process_date
上没有索引,它可能需要进行 2 次全表扫描。
编写既简单又高效的东西的困难在于,任何包含排名或排序的表格视图也不允许修改。
这是一种替代方法,使用 PL/SQL,在某些情况下可能会更有效,但显然可读性较差。
DECLARE
CURSOR delete_cur IS
SELECT /*+ FIRST_ROWS(1) */
NULL
FROM daily_statistics
ORDER BY process_date DESC
FOR UPDATE;
trash CHAR(1);
BEGIN
OPEN delete_cur;
FETCH delete_cur INTO trash;
IF delete_cur%FOUND THEN
DELETE FROM daily_statistics WHERE CURRENT OF delete_cur;
END IF;
CLOSE delete_cur;
END;
/
另请注意,如果有多个行具有相同的 process_date
值,这可能会产生与您的语句不同的结果。要使其处理重复项需要更多的复杂性:
DECLARE
CURSOR delete_cur IS
SELECT /*+ FIRST_ROWS(1) */
process_date
FROM daily_statistics
ORDER BY process_date DESC
FOR UPDATE;
del_date DATE;
next_date DATE;
BEGIN
OPEN delete_cur;
FETCH delete_cur INTO del_date;
IF delete_cur%FOUND THEN
DELETE FROM daily_statistics WHERE CURRENT OF delete_cur;
END IF;
LOOP
FETCH delete_cur INTO next_date;
EXIT WHEN delete_cur%NOTFOUND OR next_date <> del_date;
DELETE FROM daily_statistics WHERE CURRENT OF delete_cur;
END LOOP;
CLOSE delete_cur;
END;
/
【讨论】:
什么样的低效率问题让一个人写这段代码而不是问题中写的4行代码,我真的很想学习? @BurnAfterReading:如果表很大,并且列上没有索引,那么 4 行删除可能效率很低,对表进行两次全表扫描。更复杂的代码只需要进行一次扫描,尽管它可能需要进行更大的排序。我并不是在提倡它总体上更好,只是它是完成任务的另一种方式。 @Dave Costa 谢谢Mr.Costa,有多少行可以叫large
?
用更好的方法发布了第二个答案。保留这个原样,因为它已经被点赞和评论。
@BurnAfterReading:我没有确切的数字可以给出。进行全表扫描的速度取决于您的硬件和配置,因此将工作减半值得让代码更复杂的点会因环境而异。【参考方案2】:
我知道有一种我没有想到的更好的方法。
delete from daily_statistics
where rowid = (
select max(rowid) keep (dense_rank first order by process_date desc)
from daily_statistics
);
同样,这只会删除一行,即使有多行具有最大值,因此根据您的数据,它可能会产生与原始查询不同的结果。
【讨论】:
以上是关于我可以在 Oracle 中删除没有子选择的最新记录吗?的主要内容,如果未能解决你的问题,请参考以下文章