以下是有问题的示例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
答案 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
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检查它......