【问题标题】:Can I delete the most recent record without sub-select in Oracle?我可以在 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 注意我提到效率并不是我真正关心的问题。

标签: sql oracle subquery


【解决方案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
);

同样,这只会删除一行,即使有多行具有最大值,因此根据您的数据,它可能会产生与原始查询不同的结果。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-12-11
    • 2013-06-19
    • 1970-01-01
    • 1970-01-01
    • 2021-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多