根据这个Oracle documentation,我可以假设优化器推迟了硬解析,并且在第一次执行预准备语句之前它不会生成执行计划:
"答案是一种称为绑定偷看的现象。之前,当您将绑定变量值设置为' NY'运行该查询时,优化器必须首次进行硬解析,同时这样做会查看绑定变量以查看值是多少被分配给它。"
但是当使用绑定参数为预准备语句执行EXPLAIN PLAN时,我们会得到一个执行计划。在他的网站上,Markus Winand说:
"使用绑定参数时,优化程序没有可用于确定其频率的具体值。然后它假设一个相等的分布,并始终获得相同的行计数估计和成本值。最后,它将始终选择相同的执行计划。"
哪一个是真的?使用均匀分布值模型准备语句时是否生成执行计划,或者是否将硬解析推迟到第一个执行时间。
答案 0 :(得分:4)
这个讨论忽略了关于绑定变量,解析和绑定偷看的非常重要的一点;这是直方图!当相关列具有直方图时,绑定变量仅成为问题。如果没有直方图,则无需查看该值。 Oracle没有关于数据分布的信息,只会使用纯数学(不同的值,空值的数量,行数等)来查找相关过滤器的选择性。
绑定和直方图是逻辑上的对立面。您可以使用绑定变量为所有查询获取一个执行计划。您可以使用直方图为不同的搜索值获取不同的执行计划。绑定偷看试图克服这个问题。但它并没有做得很好。实际上,许多人将绑定偷看功能描述为"一个错误"。 Oracle 11g中出现的自适应游标共享可以更好地解决这个问题。
实际上我看到很多直方图。我通常禁用直方图(方法opt =>'对于所有列大小为1',并且只在我真正需要它们时创建它们。
然后是最初的问题:" Oracle在解析预准备语句时是否选择了默认执行计划?" 解析不是一项活动。解析涉及语法检查,语义分析(表和列是否存在,您是否可以访问表),查询重写(例如,如果我们使用过滤器a = b和b,Oracle可能会以更好的方式重写查询) = c,然后Oracle可以添加过滤器a = c),当然还可以找到执行计划。我们实际上在不同类型的解析之间存在差异 - 软解析和硬解析。硬解析是Oracle还必须为查询创建执行计划的地方。这是一项非常昂贵的活动。
回到问题。如果您使用绑定变量,解析并不是非常关心。不同之处在于,如果使用bind,则可能只需要进行软解析。使用绑定变量,每次运行它时,查询看起来都是一样的(因此得到相同的hash_value)。当您运行查询时,Oracle将检查(在库高速缓存中)以查看是否已准备好您的查询的执行计划。这不是一个默认计划,而是一个已经存在的计划,因为其他人已经执行了相同的查询(并使Oracle进行了硬解析,为此查询生成执行计划)并且执行计划已经过时了缓存了。这不是默认计划。它只是解析时优化器被认为是查询的最佳选择的计划。 当你来到Oracle 12c时,它实际上变得更加复杂。在12中,Oracle有自适应执行计划 - 这意味着执行计划有另一种选择。它可以从嵌套循环开始,如果它意识到它的基数估计错误,它可以在查询执行过程中切换到散列连接。它还有一些叫做自适应统计和sql计划指令的东西。所有这些都是为了让优化器和Oracle在运行SQL时做出更好的选择: - )
答案 1 :(得分:3)
第一次绑定偷看实际上是在第一次执行时发生的。计划优化被推迟,它不会在准备阶段发生。后来又发生了另一次看法。通常对于VARCHAR2,当您绑定两个完全不同的值(即,第一个值的长度为1个字节,后来是10个字节)时,优化器会再次查看,并且可能会生成新的计划。在Oracle 12中,它进一步扩展,它具有自适应连接方法。所以优化器会建议NESTED LOOP,但是当它实际执行的行数超过估计的行数之后会立即切换到HASH加入。它不像自适应游标共享,你需要先犯错才能产生新的执行计划。
准备陈述也是一件非常重要的事情。因为这些只是重新执行与第一次执行时创建的游标相同的游标。他们将始终执行相同的计划,不可能有任何适应。对于自适应和替代执行计划,必须至少进行SOFT解析。因此,如果计划从共享池中老化或因任何原因无效。
解释计划不是游标,它永远不会尊重绑定变量。它只显示光标,你可以看到绑定变量信息。
您可以在V $ SQL_BIND_CAPTURE中找到有关捕获的绑定值的实际信息。
答案 2 :(得分:1)
根据Tom Kyte绑定偷看发生在硬解析阶段,它与你帖子中的第一个引号相吻合。在11g中,优化器甚至能够为不同的绑定范围提出不同的计划,这与第二个引用直接相矛盾(尽管公平地说他是在讨论绑定变量而不是专门偷看)。
应用程序中的查询使用绑定值将其驱动到一个计划或另一个计划。只有当计划在两个截然不同的执行路径之间翻转时,并且对于某些用户段,您才有一个非常糟糕的计划。在这种情况下,Oracle Database 11g可能是您的正确答案,因为它可以容纳多个计划。
答案 3 :(得分:1)
通常,自适应游标共享最好描述从11g开始的Oracle行为(参见http://docs.oracle.com/database/121/TGSQL/tgsql_cursor.htm#BGBJGDJE)
特别适用于JDBC(精简驱动程序):使用PreparedStatements时,在执行步骤之前不会生成任何计划。
请参阅以下示例:
String metrics[] = new String[OracleConnection.END_TO_END_STATE_INDEX_MAX];
metrics[OracleConnection.END_TO_END_MODULE_INDEX] = "adaptiveCSTest";
((OracleConnection) conn).setEndToEndMetrics(metrics, (short) 0);
String getObjectNames = "select object_name from key.objects where object_type=?";
PreparedStatement objectNamesStmt = conn.prepareStatement(getObjectNames);
// module set, but statement not parsed
objectNamesStmt.setString(1, "CLUSTER");
// same state
ResultSet rset1 = objectNamesStmt.executeQuery();
// statement parsed and executed