确保 Oracle 查询中顺序一致的最佳方法是什么?

What is the best way to ensure consistent ordering in an Oracle query?

我有一个程序需要 运行 查询许多非常大的 Oracle 表(最大的有数千万行)。这些查询的输出被送入另一个进程(作为副作用)可以记录查询的进度(即获取的最后一行)。

如果任务因为某种原因中途停止,能重新启动就好了。为此,查询必须以一致的顺序 return 行,因此必须对其进行排序。显而易见的事情是对主键进行排序;但是,与未排序的解决方案相比,在性能(索引访问)方面可能会受到惩罚。鉴于重启可能永远不会发生,这是不可取的。

是否有一些技巧可以以其他方式确保一致的排序?在这种情况下,对于保持性能还有其他建议吗?

编辑:我环顾四周,看到提到了 "order by rowid"。这有用甚至可能吗?

EDIT2:我正在添加一些基准:

所以任何 order by 都会对性能产生严重影响,而使用 rowid 几乎没有什么区别。公认的答案是 - 没有简单的方法可以做到。

我能想到的最好的建议是减少发生可能停止进程的问题的可能性,这意味着保持代码简单。没有游标,没有提交,没有试图移动部分数据,只是直接 SQL 语句。

除非完全重启会是一场完全无法接受的灾难,否则我会为了简单起见而根本不使用任何中途重启代码。

如果你想要一些排序,而查询到的数据是未排序的,那么无论如何你都需要对其进行排序,并花费一些资源来进行排序。
因此,至少有两种优化变体:

  1. 尽量减少排序所花费的资源;
  2. 查询已经排序的数据。

对于第一个变体,Oracle 自行计算出一个最佳变体,以最大程度地减少数据访问和总体查询时间。可以选择优化器已经使用的唯一索引中涉及的排序顺序,但这是一个非常值得怀疑的策略。

第二个变体是关于索引组织的 tables 和关于通过提示强制 Oracle 使用某些特定索引。如果您需要处理某些特定 table 中的几乎所有记录,这似乎没问题,但如果查询的选择性很高,它会显着减慢进程,即使在单个 table 上也是如此。

考虑一个 table 和代理主键,它保存具有 10 年交易历史的数据。如果您只需要前一年的数据,并且强制按主键排序,那么 Oracle 需要逐个处理所有 10 年的记录,以查找属于某一年的所有记录。
但是,如果您需要来自此 table 的 9 年数据,那么完整 table 扫描可能比基于索引的选择更快。
因此,查询的选择性是在完整 table 扫描和结果排序之间进行选择的关键。

为了存储结果和重新启动查询,一个好的解决方案是使用 Oracle Streams Advanced Queuing 来提供另一个进程。
队列中所有未处理的消息都重定向到异常队列,在那里可以单独处理。
因为您没有为选定的消息指定确切的顺序,所以我想您只需要订购来维护未处理的记录部分。如果是这样,那么对于 AQ,您根本不需要订购,甚至可以并行处理记录。

所以,最后,从我的角度来看,Buffered Queue 才是您真正需要的。

您可以跳过排序,只需使用 SET is_processed = 'Y'SET date_processed = sysdate 之类的内容更新您处理的记录。完全可重启且无需排序。

为了提高性能,您可以按 is_processed 进行分区。是的,分区键更改可能会很慢,但这完全是权衡取舍。