Oracle SQL:在内联视图中出现时,了解SYS_GUID()的行为?

时间:2014-05-09 19:36:14

标签: sql oracle

以下是有问题的示例SQL; SQL应该在任何Oracle DBMS上运行(我运行的是11.2.0.2.0)。

注意结果集中的UUID值是如何不同的(一个具有898,另一个具有899),尽管是从内联视图/ with子句内构建的。在下面你可以看到DBMS_RANDOM.RANDOM()没有这种副作用。

SQL:

WITH data AS (SELECT SYS_GUID () uuid FROM DUAL)
    SELECT uuid, uuid
      FROM data

输出:

UUID                                      UUID_1
F8FCA4B4D8982B55E0440000BEA88F11      F8FCA4B4D8992B55E0440000BEA88F11

在对比度DBMS_RANDOM 中,结果相同

SQL:

WITH data AS (SELECT DBMS_RANDOM.RANDOM() rand FROM DUAL)
SELECT rand, rand
  FROM data

输出:

RAND    RAND_1
92518726    92518726

更有趣的是我可以通过包含对DBMS_RANDOM.RANDOM的调用来改变行为/稳定sys_guid:

WITH data AS (
        SELECT SYS_GUID () uuid, 
        DBMS_RANDOM.random () rand 
        FROM DUAL)
SELECT uuid a,
       uuid b,
       rand c,
       rand d
  FROM data

稳定SYS_GUID的SQL小提琴: http://sqlfiddle.com/#!4/d41d8/29409

SQL Fiddle显示奇怪的SYS_GUID行为: http://sqlfiddle.com/#!4/d41d8/29411

3 个答案:

答案 0 :(得分:6)

documentation gives a reason关于为什么你会看到差异(强调我的):

  

<强>注意:

     

因为SQL是一种声明性语言,而不是命令式(或程序性)语言,你无法知道SQL语句调用的函数将运行多少次 - 即使函数是用函数写的PL / SQL,一种命令式语言。   如果您的应用程序要求函数执行一定次数,请不要从SQL语句调用该函数。改为使用光标。

     

例如,如果您的应用程序要求为每个选定的行调用函数,则打开游标,从游标中选择行,然后为每一行调用该函数。此技术保证对函数的调用次数是从游标中获取的行数。

基本上,Oracle没有指定在sql语句中调用函数的次数:它可能取决于发布,环境,访问路径以及其他因素。

但是,有一些方法可以限制查询重写,如章节Unnesting of Nested Subqueries中所述:

  

子查询不需要的东西,并将子查询的主体合并到包含它的语句的主体中,允许优化器在评估访问路径和连接时将它们一起考虑。优化器可以取消大多数子查询,但有一些例外。这些异常包括分层子查询和包含ROWNUM伪列的子查询,集合运算符之一,嵌套聚合函数或对查询块的相关引用,该查询块不是子查询的直接外部查询块。

如上所述,您可以使用ROWNUM伪列来阻止Oracle取消子查询:

SQL> WITH data AS (SELECT SYS_GUID() uuid FROM DUAL WHERE ROWNUM >= 1)
  2  SELECT uuid, uuid FROM data;

UUID                             UUID
-------------------------------- --------------------------------
1ADF387E847F472494A869B033C2661A 1ADF387E847F472494A869B033C2661A

答案 1 :(得分:2)

NO_MERGE提示“修复”它。阻止Oracle重写内联视图。

WITH data AS (SELECT /*+ NO_MERGE */
                    SYS_GUID () uuid FROM DUAL)
SELECT uuid, uuid
  FROM data

From the docs:

  

NO_MERGE提示指示优化器不要组合外部   查询和任何内联视图查询到单个查询。这个提示允许   你对访问视图的方式有更多的影响。

SQL Fiddle with the NO_MERGE hint applied

我仍然在努力理解/阐明如何以sys_guid()被调用两次的方式重写查询。也许这是一个bug;但我倾向于认为这是我自己的想法/代码中的一个错误。

答案 2 :(得分:2)

非常有趣。

我们可以使用materialize提示来修复它。

WITH data AS (SELECT /*+materialize*/SYS_GUID () uuid FROM DUAL)
    SELECT uuid, uuid
      FROM data;

1   F9440E2613761EC8E0431206460A934C    F9440E2613761EC8E0431206460A934C

从我的角度来看,如果我们只是通过添加提示来更改查询结果,就会出现Oracle错误。 也许我们不得不要求metalink检查它......