我可以在 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 中删除没有子选择的最新记录吗?的主要内容,如果未能解决你的问题,请参考以下文章

Oracle SQL序列

在 Oracle 中避免相关子查询

选择一个Id每天的最新记录 - Oracle pl sql

BigQuery:从表连接引起的子选择中删除记录

oracle - 违反完整性约束 - 找到子记录

在子查询之间的 ORACLE 连接语句中选择顶部记录