简体   繁体   English

Oracle 12c Subquery Factoring Inline View现在有不好的计划吗?

[英]Oracle 12c Subquery Factoring Inline View now has bad plan?

Update 11/2 更新11/2

After some additional troubleshooting, my team was able to tie this Oracle bug directly to a parameter change that was made on the 12c database the night before the query stopped working. 经过一些额外的故障排除后,我的团队能够将此Oracle错误直接与查询停止工作前一天在12c数据库上进行的参数更改联系起来。 After experiencing some performance issues from an application tied to this database, my team had our DBA change the OPTIMIZER_FEATURES_ENABLE parameter from 12.1.02 to 11.2.0.4 . 在遇到与此数据库12.1.02的应用程序的一些性能问题后,我的团队让我们的DBA将OPTIMIZER_FEATURES_ENABLE参数从12.1.0211.2.0.4 This fixed the performance issue for the problem application but caused the bug I have described above. 这解决了问题应用程序的性能问题,但导致了我上面描述的错误。 To verify, I've been able to replicate this same issue in a separate environment by changing this parameter. 为了验证,我已经能够通过更改此参数在单独的环境中复制相同的问题。 My DBA has filed a ticket with Oracle to have this looked at. 我的DBA已向Oracle提交了一张机票,以便对此进行查看。

As a workaround, I was able to make a slight change to my query in order to retrieve the expected results. 作为一种解决方法,我能够对我的查询稍作更改,以便检索预期的结果。 Specifically, I combined Subquery1 with Subquery2 and I moved a few predicates in Subquery1 from the WHERE clause to the JOIN (where they more properly belonged). 具体地讲,我结合Subquery1Subquery2和我在移动几个谓词Subquery1WHERE子句到JOIN (在那里它们更恰当地属于)。 This change edited my execution plan (it is slightly less efficient than what was listed before) but was enough to address the original issue. 此更改编辑了我的执行计划(它的效率略低于之前列出的效率)但足以解决原始问题。


Original Post 原帖

Firstly, let me apologize for any vagueness in this question but I'm dealing with a confidential financial system so I am forced to hide certain implementation details. 首先,让我为这个问题中的任何模糊道歉,但我正在处理一个机密的金融系统,所以我被迫隐藏某些实施细节。

Background 背景

I have an Oracle query that I put into production a long time ago that has recently stopped producing expected results coincidentally after an upgrade from 11g to 12c . 我有一个很久以前投入生产的Oracle查询,它最近在从11g升级到12c之后不久产生了预期的结果。 To my (and my production support team's) knowledge this query had been working fine for well over a year before that. 对于我(以及我的生产支持团队)的知识,这个查询在此之前已经运行了一年多。

Details 细节

The query is overly complicated and not very efficient but this is in large part because I am dealing with non-normalized tables (historically modeled after a Mainframe) and poor data input from upstream systems. 查询过于复杂且效率不高,但这在很大程度上是因为我正在处理非规范化表(历史上以大型机为模型)和来自上游系统的较差数据输入。 In order to deal with a complicated business situation I leveraged multiple levels of Subquery Factoring (the WITH statement) and then my final statement joins together two Inline Views. 为了处理复杂的业务情况,我利用了多个级别的子查询因子( WITH语句),然后我的最终语句将两个内联视图连接在一起。 The basic structure of the query without all of the complicated predicates is as follows: 没有所有复杂谓词的查询的基本结构如下:

I have 3 tables Table1 , Table2 , Table3 . 我有3个表Table1Table2Table3 Table1 is a processing table made up of records from Table2 . Table1是由表Table2的记录组成的处理表。

--This grabs a subset from Table1
WITH Subquery1 as (
   SELECT FROM Table1),

--This eliminates certain records from the first subset based on sister records 
--from the original source table 
Subquery2 as (
   SELECT FROM Subquery1
   WHERE NOT EXISTS FROM (SELECT from Table2)),

--This ties the records from Subquery2 to Table3
Subquery3 as (
   SELECT FROM Table3
   JOIN (SELECT Max(Date) FROM Table3)
   JOIN Subquery2)

--This final query evaluates subquery3 in two different ways and 
--only takes those records which fit the criteria items from both sets
SELECT FROM 
(SELECT FROM Subquery3)             -- Call this Inline View A
JOIN (SELECT FROM Subquery3)        -- Call this Inline View B

The final query is pretty basic: 最后的查询非常基本:

   SELECT A.Group_No, B.Sub_Group, B.Key, B.Lob               
   FROM   (SELECT Group_No, Lob, COUNT(Sub_Group) 
           FROM   Subquery3 
           GROUP BY Group_No, Lob
           HAVING COUNT(Sub_Group) = 1) A 
   JOIN (SELECT Group_No, Sub_Group, Key, Lob
         FROM   Subquery3 
         WHERE  Sub_Group LIKE '0000%') B 
   ON A.Group_No = B.Group_No
   AND A.Lob = B.Lob

Problem 问题

If I edit the final query to remove the second Inline View and evaluate the output of the A inline view, I come away with 0 returned rows . 如果我编辑最终查询以删除第二个内联视图并评估A内联视图的输出,我将返回0返回的行 I've manually evaluated the records for each individual subquery and can confirm this is an expected result. 我手动评估了每个子查询的记录,并确认这是预期的结果。

Likewise, if I edit the final query to produce the output of only the 'B' inline view, I come away with 6 returned rows . 同样,如果我编辑最终查询以仅生成“B”内联视图的输出,那么我将返回6个返回的行 Again, I've manually evaluated the data and this is exactly as expected. 我再次手动评估了数据,这与预期完全一致。

Now when joining these two subsets (Inline View A and Inline View B ) together, I would expect that the final query result would be 0 rows (since an inner join between a full set and an empty set produces no matches). 现在,当将这两个子集(内联视图A和内联视图B )连接在一起时,我希望最终查询结果为0行(因为完整集和空集之间的内部连接不会产生匹配)。 However, when I run the entire query with the inner join as described above, I am getting back 1158 rows ! 但是,当我如上所述使用内部联接运行整个查询时, 我将返回1158行

I have reviewed the Execution Plan but nothing jumps out at me: 我已经审查了执行计划,但没有任何事情发生在我身上:

执行计划1 执行计划2

Questions 问题

Clearly I have done something to confuse the Oracle Optimizer and the updated query plan is pulling back a much different query than the one I have submitted. 很明显,我已经做了一些事情来混淆Oracle Optimizer,而更新的查询计划正在拉回一个与我提交的查询截然不同的查询。 My best guess is that with all of these temporary views floating around within the same query, I have confused Oracle into evaluating some set before one that it depends upon. 我最好的猜测是,所有这些临时视图都在同一个查询中浮动,我让Oracle混淆了在依赖它之前评估一些集合。

To this day I've been unable to locate the official Oracle documentation around the WITH statement so I've never been completely confident about the order that subqueries are evaluated. 到目前为止,我一直无法找到围绕WITH语句的官方Oracle文档,因此我从未对子查询的评估顺序充满信心。 I did notice in searching SO ( can't find it now ) someone mentioned that a factored subquery cannot refer to another factored query. 我确实注意到搜索SO( 现在找不到 )有人提到一个因子子查询不能引用另一个因子查询。 I've never known this to be true before but the bizarre output above is making me wonder if I had only been lucky before with this query? 我以前从来不知道这是真的,但上面那个奇怪的输出让我想知道我之前是否只有这个查询才幸运?

Can anyone explain the behavior I am seeing? 谁能解释我所看到的行为? Am I attempting to do something obviously incorrect with this query plan? 我是否尝试使用此查询计划做一些明显不正确的事情? Or alternatively, is there any chance that something changed between 11g and 12c that could explain why the behavior of this query might have changed? 或者,是否有可能在11g和12c之间发生变化,这可以解释为什么这个查询的行为可能已经改变了?

This sounds like a "wrong results" bug in Oracle. 这听起来像Oracle中的“错误结果”错误。 These bugs are usually extremely specific to the version and the features you are using. 这些错误通常非常特定于您使用的版本和功能。 There's nothing obviously wrong with the queries or execution plan you posted. 您发布的查询或执行计划没有明显错误。

You have two ways of handling this: 您有两种处理方式:

  1. Try to find the precise bug. 尝试找到准确的错误。 What you're doing with common table expressions looks fine. 你正在使用常见的表表达式看起来很好。 There are some rare times when your query is technically invalid, you get "lucky" in one version and it works, and when you upgrade it fails. 在极少数情况下,您的查询在技术上无效,您在一个版本中获得“幸运”并且它可以正常运行,并且在升级时它失败。 But when that happens the new version usually throws an error, not return wrong results. 但是当发生这种情况时,新版本通常会抛出错误,而不会返回错误的结果。 There's probably some extremely weird, specific combination of features you're using that's causing the issue. 可能有一些非常奇怪的,特定的功能组合正在引起问题。 To find the real issue you need to massively simplify the query until you can make the smallest possible change and see the problem appear and disappear. 要找到真正的问题,您需要大规模简化查询,直到您可以进行尽可能小的更改并看到问题出现和消失。 You'll also want to remove all objects and only use DUAL . 您还需要删除所有对象并仅使用DUAL This process can take hours. 此过程可能需要数小时。 At the end, when you're left with only a few lines of code, either post them here, look on Oracle Support, or create a Service Request. 最后,当您只剩下几行代码时,请在此处发布,查看Oracle支持或创建服务请求。
  2. Avoid the bug. 避免这个bug。 Even if you go through the above steps there may not be a fix anyway. 即使您完成上述步骤,也可能无法解决问题。 Sometimes the best work-around is to do something differently. 有时,最好的解决方法是采取不同的做法。 It's nice to get to the bottom of every problem but you don't always have time. 能够深入了解每个问题但你并不总是有时间。 Instead, try re-writing the query in syntactically different but logically equivalent ways. 相反,尝试以语法不同但逻辑上等效的方式重写查询。 Remove some or all of the common table expressions, maybe even repeat some SQL. 删除部分或全部公用表表达式,甚至可能重复一些SQL。 But be sure to leave a comment warning future programmers of why you're doing things in a weird way. 但请务必发表评论,警告未来的程序员,为什么你会以一种奇怪的方式做事。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM