【问题标题】:Why do I need to include rownum field in this Oracle SQL query while using dbms_random.value function?为什么在使用 dbms_random.value 函数时需要在此 Oracle SQL 查询中包含 rownum 字段?
【发布时间】:2013-02-12 12:22:21
【问题描述】:

我从一些表中抽取了一个随机样本,并注意到根据我编写查询的方式,它不起作用。显然,我没有使用 all_tab_columns,我只是提供了一个适用于 vanilla 实例 (9.2.0.8) 的示例。

为什么会这样?

select * from (
    select 
        floor(dbms_random.value(0,1000)) as randomval
    from 
        all_tab_columns where rownum < 10000
) where randomval > 200 and randomval < 300;

但这不会返回任何结果。

select * from (
    select 
        floor(dbms_random.value(0,1000)) as randomval
    from 
        all_tab_columns 
) where randomval > 200 and randomval < 300;

查询中

编辑:澄清什么不起作用。

编辑:添加版本 9.2.0.8

【问题讨论】:

  • 你能定义“不起作用”吗?第二个查询肯定会返回行。任一查询将返回的行数未知(尽管第一个查询不可能返回超过 10,000 行)。
  • 第二个根本不为我返回任何行。我在 sqlplus 中得到“没有选择行”。
  • 随机性的本质是随机的。因此,内部查询很可能返回 201 到 299 之间的无值。在 ROWNUM 上添加过滤器实际上可能会使这种结果更有可能而不是更少。所以我认为你不太可能有一个可重现的测试用例。
  • APC - 我使用了一个包含 600 万个结果的表,并尝试了 rownum
  • rownum 将阻止外部谓词推入内联视图(即,它阻止查询被重写为peter 显示的那个)。您还应该看到 where rownum &gt; 0 的“好”结果(或 no_merge / no_push_pred 提示也可能)。在 9i(特别是 9.2.0.2-.8)中存在一个错误,涉及 dbms_random 的查询合并视图(我认为这是这里发生的事情)

标签: sql oracle random sample rownum


【解决方案1】:

我没有明确的答案,但我有一个理论......

我的猜测是您的第二个查询正在为此优化:

select * 
  from all_tab_columns 
 where floor(dbms_random.value(0,1000))> 200 
   and floor(dbms_random.value(0,1000)) < 300;

并且在您的内联视图中以某种方式具有 rownum 标准会阻止该优化。

这也可以解释为什么我们中的一些人(包括我在内)无法看到您描述的问题 - 因为我们使用不同版本的 Oracle,并且查询对我们的优化方式不同。

编辑

经过一番谷歌搜索,我发现了this AskTom question,这似乎是相关的。 Tom Kyte 的回答以这行结尾:

从 SQL 调用函数时,最好不要依赖频率 函数以什么顺序或其他方式被调用。简而言之,假设 没有。请记住 - SQL 重写开始,我们重写您的 SQL 所有的时间。不要依赖副作用

【讨论】:

  • “并且以某种方式具有 rownum 标准”。 rownum 防止谓词推送(即谓词不能推送过去 rownum,因为它可能会改变答案)例如 select * from (select * from all_objects where rownum &gt; 0) where object_name = 'DUAL' and owner = 'SYS' 不会被重写以将 object_name/type 推送到内部视图中(因此您将在该示例中看到它必须首先全面扫描整个视图并且性能不佳)。使用内部选择与 where 子句中的 rownum 也会看到相同的效果。
【解决方案2】:

我认为@Peter 有点意思,但需要更多解释。

在 Oracle 中,函数可以是 DETERMINISTIC 也可以不是。这意味着该函数在使用相同的参数调用时返回相同的值。所以,sqrt() 是确定性的; dbms_random() 不是。其他数据库使用术语 STABLE 与 VOLATILE 来表示这两个类别。

如果我不得不猜测,Oracle 引擎足够聪明,知道以下查询应该返回行:

select * from (
    select 
        dbms_random.value(0,1000) as randomval
    from 
        all_tab_columns 
) where randomval > 200 and randomval < 300;

但是,我怀疑它会因为floor() 而出错。也就是说,编译器检测到一个“稳定”函数,所以它只是查找以前的值。而且,它使用缓存的值,多次调用函数时短路。

如果这是正确的,那么上面的查询将返回行。如果是这样,那么我会称数据库的这个“特性”为错误——不过,可能在某个地方有一位数据库优化工程师,他会认为这是一个非常有用的特性。

【讨论】:

    【解决方案3】:

    您的两个查询都适用于 Oracle 11g R2。 ROWNUM 在Oracle 中是伪列,有兴趣的可以阅读。

    【讨论】:

      猜你喜欢
      • 2013-10-22
      • 2013-12-19
      • 1970-01-01
      • 2011-01-05
      • 1970-01-01
      • 2018-03-18
      • 2020-05-11
      • 1970-01-01
      • 2018-01-21
      相关资源
      最近更新 更多