【问题标题】:Oracle 10G - Query using rownum stopped working after migration from 9iOracle 10G - 从 9i 迁移后使用 rownum 的查询停止工作
【发布时间】:2010-08-03 07:42:59
【问题描述】:

我们最近刚刚将数据库从 9i 迁移到 10G (是的..迟到总比没有好 - 迁移到 11g 目前不是一个选择:-))

我的 Oracle 10G DB 的详细信息是:-

Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod
PL/SQL Release 10.2.0.1.0 - Production
CORE    10.2.0.1.0      Production

自从那次搬家以来,我面临着一个非常奇怪的问题。 在 9i 上过去和现在都可以正常工作的查询在 10G 上无法正常工作。

我确实搜索了与 rownum 相关的其他 SO 问题,但真的找不到类似的东西。

SQL 查询是:-

SELECT * FROM 
( SELECT field1, field2 , field3, field4, field5, field6, field7, to_char(rownum) field8
  FROM
    ( SELECT 
        field1,
        field2,
        field3,
        field4,
        field5,
        field6,
        field7,
        ''
      FROM
      .......REST OF MY COMPLEX INNER QUERY
  )
) 
WHERE field8 BETWEEN 21 AND 30;

基本上,21 / 30 是传递给分页查询的记录索引的数字,在 9i 中,此查询按预期工作,仅返回指定的数据集。

但是在 10G 中,同样的查询根本不起作用 - 总是返回 0 条记录。

如果我评论查询的rownum相关部分:-

to_char(rownum) field8  and
WHERE field8 BETWEEN 21 AND 30;

然后我得到了整个结果集,这很好。 但是由于我的意图是使用 rownum 进行分页,所以整个目的都失败了。

有谁知道这个查询停止使用 10G 的任何原因。 我尝试查找 rownum 实现的任何更新,但还没有真正找到任何有用的东西。

编辑:- 在进行调试时,我遇到了一些对我来说毫无意义的事情。 我在下面输入整个查询,因为没有它我无法解释。

SELECT * FROM 
( SELECT field1, field2 , field3, field4, field5, field6, field7, to_char(rownum) field8 from 
 ( SELECT PM.POLICY_NO field1
   ,PM.INSURED_CODE field2
   ,PM.INSURED_NAME field3
   ,TO_CHAR(PM.POLICY_EFFECTIVE_DATE,'DD/MM/YYYY') field4
   ,TO_CHAR(PM.POLICY_EXPIRATION_DATE,'DD/MM/YYYY') field5
   ,'' field6
   ,'' field7
   ,'' field8
   FROM POLICY_MAIN PM
   ,POLICY_ENDORSEMENT_MAIN PEM
   ,MASTER_UW_LOB_CLASS MAS
   WHERE PM.POLICY_NO = PEM.POLICY_NO
   AND PM.POLICY_NO LIKE UPPER('%%')
   AND PM.INSURED_CODE LIKE UPPER('%%')
   AND PM.SOURCE_OF_BUSINESS LIKE UPPER('%%')
   AND PM.POLICY_TYPE IS NULL
   AND PM.POLICY_STATUS = 'POST'
   AND PM.POLICY_LOB = MAS.UW_LOB_CODE
   AND MAS.UW_CLASS_CODE LIKE UPPER('AUTO')
   AND PEM.POLICY_ENDORSEMENT_NO =
    (SELECT MAX(PEM2.POLICY_ENDORSEMENT_NO)
     FROM   POLICY_ENDORSEMENT_MAIN       PEM2
     WHERE  PEM.POLICY_NO                 = PEM2.POLICY_NO
     ***AND    PEM.ENDORSEMENT_STATUS        = 'POST'***
     )
   ***order by 1 ASC***
  )
) 
WHERE field8 BETWEEN 21 AND 40

参考最内层子查询中 *** 之间标记的行。

  1. 如果我从我的查询中评论这一行,查询工作正常。

    AND PEM.ENDORSEMENT_STATUS = 'POST'

  2. 如果我从我的查询中评论这一行并且其他所有内容都与原始内容保持不变,那么查询也可以正常工作

    按 1 ASC 排序

与 rownum 相关的早期观点仍然适用,但单独评论这些行似乎使 rownum 无关紧要,整个查询工作正常(除了现在结果在逻辑上不同的事实)

我很困惑。至少可以说!!!

编辑 2:

为上述查询添加执行计划

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=19 Card=1 Bytes=114)

   1    0   VIEW (Cost=19 Card=1 Bytes=114)
   2    1     COUNT
   3    2       FILTER
   4    3         VIEW (Cost=17 Card=1 Bytes=128)
   5    4           SORT (ORDER BY) (Cost=17 Card=1 Bytes=130)
   6    5             TABLE ACCESS (BY INDEX ROWID) OF 'POLICY_ENDORSEMENT_MAIN' (TABLE) (Cost=2 Card=1 Bytes=39)
   7    6               NESTED LOOPS (Cost=16 Card=1 Bytes=130)
   8    7                 NESTED LOOPS (Cost=14 Card=1 Bytes=91)
   9    8                   TABLE ACCESS (FULL) OF 'POLICY_MAIN' (TABLE) (Cost=14 Card=1 Bytes=82)
  10    8                   INDEX (UNIQUE SCAN) OF 'PK_MASTER_UW_LOB_CLASS' (INDEX (UNIQUE)) (Cost=0 Card=1 Bytes=9)
  11    7                 INDEX (RANGE SCAN) OF 'PK_POLICY_ENDORSEMENT_MAIN' (INDEX (UNIQUE)) (Cost=1 Card=1)
  12    3         SORT (AGGREGATE)
  13   12           FILTER
  14   13             INDEX (RANGE SCAN) OF 'PK_POLICY_ENDORSEMENT_MAIN' (INDEX (UNIQUE)) (Cost=2 Card=2 Bytes=68)

编辑 3:

与上面完全相同的查询,但如果我删除了

ORDER BY 1 ASC

子句,然后按预期检索结果。 此查询的不带 order by 的 PLAN 如下

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=18 Card=1 Bytes=114)
   1    0   VIEW (Cost=18 Card=1 Bytes=114)
   2    1     COUNT
   3    2       FILTER
   4    3         TABLE ACCESS (BY INDEX ROWID) OF 'POLICY_ENDORSEMENT_MAIN' (TABLE) (Cost=2 Card=1 Bytes=39)
   5    4           NESTED LOOPS (Cost=16 Card=1 Bytes=130)
   6    5             NESTED LOOPS (Cost=14 Card=1 Bytes=91)
   7    6               TABLE ACCESS (FULL) OF 'POLICY_MAIN' (TABLE) (Cost=14 Card=1 Bytes=82)
   8    6               INDEX (UNIQUE SCAN) OF 'PK_MASTER_UW_LOB_CLASS' (INDEX (UNIQUE)) (Cost=0 Card=1 Bytes=9)
   9    5             INDEX (RANGE SCAN) OF 'PK_POLICY_ENDORSEMENT_MAIN' (INDEX (UNIQUE)) (Cost=1 Card=1)
  10    3         SORT (AGGREGATE)
  11   10           FILTER
  12   11             INDEX (RANGE SCAN) OF 'PK_POLICY_ENDORSEMENT_MAIN' (INDEX (UNIQUE)) (Cost=2 Card=2 Bytes=68)

请注意,这两个计划之间唯一真正的区别是,不工作的计划在第 3 步之后有以下两个附加步骤,因为这些步骤在没有 order by 的查询中不存在 - 这工作正常。

正如预期的那样,第 5 步是完成数据排序的步骤。

   4    3         VIEW (Cost=17 Card=1 Bytes=128)
   5    4           SORT (ORDER BY) (Cost=17 Card=1 Bytes=130)

似乎第 4 步可能是由于排序而创建的附加视图。

为什么这会阻止 rownum 逻辑工作是我仍在努力掌握的。

任何帮助表示赞赏!

EDIT 4 - 来自 9i 环境的原始查询计划

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   VIEW
   2    1     COUNT
   3    2       VIEW
   4    3         SORT (ORDER BY)
   5    4           FILTER
   6    5             TABLE ACCESS (BY INDEX ROWID) OF 'POLICY_MAIN'
   7    6               NESTED LOOPS
   8    7                 NESTED LOOPS
   9    8                   TABLE ACCESS (FULL) OF 'POLICY_ENDORSEMENT_MAIN'
  10    8                   INDEX (RANGE SCAN) OF 'PK_MASTER_UW_LOB_CLASS' (UNIQUE)
  11    7                 INDEX (RANGE SCAN) OF 'PK_POLICY_MAIN' (UNIQUE)
  12    5             SORT (AGGREGATE)
  13   12               FILTER
  14   13                 INDEX (RANGE SCAN) OF 'PK_POLICY_ENDORSEMENT_MAIN' (UNIQUE)

【问题讨论】:

  • 当您编辑查询时,Oracle 会重新解析查询,并且通常会提出新的计划,这些计划可能会或可能不会显示您遇到的问题。你能发布查询计划吗?
  • @Jeffrey - 编辑了问题并添加了查询计划。必须解释一下,当谈到 Oracle 时,我是一个新手,对我来说有点希腊语和拉丁语。虽然现在查找 ASK TOM 以尝试理解它:-)
  • 查看该计划,Oracle 似乎取消了过滤子查询(针对 PEM2)——请注意,SORT (ORDER BY) 比过滤器更深。这可以解释为什么查询不起作用;在过滤掉行之前分配 ROWNUM。尝试使用以下 Jeffery Kemp 演示 NO_MERGE 的示例使用 NO_UNNEST 提示。
  • 另外,考虑在针对 PEM2 的子查询中使用 PUSH_SUBQ 提示。
  • @Adam - 尝试了两个版本 - 但这对结果也没有任何影响。只是为了确认一下,这个执行计划是从下到上阅读的——对吗?顺便说一句,我对 rownum 本身的查询,即 SELECT field1, field2 , field3, field4, field5, field6, field7, to_char(rownum) field8.... 只是一个 select * from table - 其中 table 是内部查询。优化器是否可能并不总是首先评估内部查询?

标签: oracle10g rownum


【解决方案1】:

正如 Adam 所建议的,子查询在应用排序和 ROWNUM 之后过滤结果。

我认为您需要通过使用 PUSH_SUBQ 提示来强制过滤该子查询:

SELECT * FROM 
( SELECT field1, field2 , field3, field4, field5, field6, field7,
         ROWNUM field8 from 
 ( SELECT PM.POLICY_NO field1
   ,PM.INSURED_CODE field2
   ,PM.INSURED_NAME field3
   ,TO_CHAR(PM.POLICY_EFFECTIVE_DATE,'DD/MM/YYYY') field4
   ,TO_CHAR(PM.POLICY_EXPIRATION_DATE,'DD/MM/YYYY') field5
   ,'' field6
   ,'' field7
   ,'' field8
   FROM POLICY_MAIN PM
   ,POLICY_ENDORSEMENT_MAIN PEM
   ,MASTER_UW_LOB_CLASS MAS
   WHERE PM.POLICY_NO = PEM.POLICY_NO
   AND PM.POLICY_NO LIKE UPPER('%%')
   AND PM.INSURED_CODE LIKE UPPER('%%')
   AND PM.SOURCE_OF_BUSINESS LIKE UPPER('%%')
   AND PM.POLICY_TYPE IS NULL
   AND PM.POLICY_STATUS = 'POST'
   AND PM.POLICY_LOB = MAS.UW_LOB_CODE
   AND MAS.UW_CLASS_CODE LIKE UPPER('AUTO')
   AND PEM.POLICY_ENDORSEMENT_NO =
    (SELECT /*+ PUSH_SUBQ*/
            MAX(PEM2.POLICY_ENDORSEMENT_NO)
     FROM   POLICY_ENDORSEMENT_MAIN       PEM2
     WHERE  PEM.POLICY_NO                 = PEM2.POLICY_NO
     AND    PEM.ENDORSEMENT_STATUS        = 'POST'
     )
   order by 1 ASC
  )
) 
WHERE field8 BETWEEN 21 AND 40

我还从 ROWNUM 中删除了 TO_CHAR - 您想使用数字进行范围比较。

编辑

尝试 #2 - 改用 CTE:

WITH q AS
( SELECT /*+MATERIALIZE*/
         field1, field2 , field3, field4, field5, field6, field7,
         ROWNUM field8 from 
 ( SELECT PM.POLICY_NO field1
   ,PM.INSURED_CODE field2
   ,PM.INSURED_NAME field3
   ,TO_CHAR(PM.POLICY_EFFECTIVE_DATE,'DD/MM/YYYY') field4
   ,TO_CHAR(PM.POLICY_EXPIRATION_DATE,'DD/MM/YYYY') field5
   ,'' field6
   ,'' field7
   ,'' field8
   FROM POLICY_MAIN PM
   ,POLICY_ENDORSEMENT_MAIN PEM
   ,MASTER_UW_LOB_CLASS MAS
   WHERE PM.POLICY_NO = PEM.POLICY_NO
   AND PM.POLICY_NO LIKE UPPER('%%')
   AND PM.INSURED_CODE LIKE UPPER('%%')
   AND PM.SOURCE_OF_BUSINESS LIKE UPPER('%%')
   AND PM.POLICY_TYPE IS NULL
   AND PM.POLICY_STATUS = 'POST'
   AND PM.POLICY_LOB = MAS.UW_LOB_CODE
   AND MAS.UW_CLASS_CODE LIKE UPPER('AUTO')
   AND PEM.POLICY_ENDORSEMENT_NO =
    (SELECT MAX(PEM2.POLICY_ENDORSEMENT_NO)
     FROM   POLICY_ENDORSEMENT_MAIN       PEM2
     WHERE  PEM.POLICY_NO                 = PEM2.POLICY_NO
     AND    PEM.ENDORSEMENT_STATUS        = 'POST'
     )
   order by 1 ASC
  )
) 
SELECT * from q
WHERE field8 BETWEEN 21 AND 40

【讨论】:

  • @Jeffrey - 我已经按照之前的建议尝试了 push_subq,但它也没有工作。只是为了确保,我也尝试了您的查询 - 但它也不起作用。我无法将其粘贴在下面,但您的查询的查询计划与我在 EDIT 2 中发布的查询计划几乎完全相同,除了“3 2 FILTER”行,这是唯一缺少的步骤。在步骤 4 中创建的附加视图 - “4 3 VIEW (Cost=17 Card=1 Bytes=128)”是搞乱 rownum 的视图。是否有一些数据库设置可能导致提示 PUSH_SUBQ 被忽略?
  • BRILLIANT - CTE 版本完全符合预期!!实际上,我什至尝试删除 MATERIALIZE 提示,它仍然可以完全按预期工作,即使用 CTE 的查询语法的更改使子查询首先评估,因此为 rownum 提供值 - 从而使 where 子句按预期工作!!当我比较这 2 个查询计划时——我的原始查询计划,即 EDIT2 与 CTE 的计划,唯一的区别是我计划中步骤 3 的“过滤”步骤现在发生在步骤 5 中,这一切都不同了。你能解释一下为什么吗?
  • 注意,您可能会发现,如果没有 MATERIALIZE 提示,Oracle 可能会选择稍后(例如在更高版本中)不实现查询。
  • 在 Oracle 中使用查询提示是个好主意吗?我来自 SQL Server 背景,通常认为大量使用 DB 提示并不是一个好主意...由于不同版本的提示解释不同或导致非最佳查询计划的提示 - 在 SQL Server 中,我曾经理解优化器最了解的情况。在预言机世界中,使用提示是常见的做法吗?
  • 不,一般来说,避免提示您给出的原因是一个很好的经验法则。但是,在这种情况下,提示的使用完全符合其制定的目的——我们希望优化器在未来选择另一个计划——所以我们添加提示以确保.
【解决方案2】:

听起来 Oracle 正在将内联视图合并到主查询中,因此 field8(基于 ROWNUM)计算得太晚了。我自己还没有看到这种情况发生,但如果发生这种情况,您可以尝试添加这样的 NO_MERGE 提示:

SELECT /*+ NO_MERGE(vw) */ * FROM 
( SELECT field1, field2 , field3, field4, field5, field6, field7, to_char(rownum) field8
  FROM
    ( SELECT 
        field1,
        field2,
        field3,
        field4,
        field5,
        field6,
        field7,
        ''
      FROM
      .......REST OF MY COMPLEX INNER QUERY
  )
) vw
WHERE field8 BETWEEN 21 AND 30;

(顺便说一句,当您在 WHERE 子句中将它视为数字时,为什么要在 ROWNMUM 上使用 TO_CHAR?)

【讨论】:

  • 将尝试使用 nomerge 提示并进一步更新。关于 to_char,这个查询实际上是带有 union all 子句的更大查询的一部分,其中最后一列应该是一个字符串 - 因此 to_char - 我删除了查询的不相关部分,只是为了让它更容易理解。
  • 你的回答绝对有道理,我尝试了 nomerge 提示但没有骰子:-( :-(
  • @InSane,尝试使用 /*+MATERIALIZE*/ 提示 - 并将提示放在内部选择中,而不是外部选择中。
  • @Jeffrey - 感谢您的投入!也尝试了你的建议。没运气。在调试过程中,我发现了更多(奇怪的!)。将该信息作为“编辑”添加到原始帖子中。
  • 内部查询中的排序(ORDER BY)可能要求优化器不合并内部查询,这可能是它在一个版本中工作的原因;但是,Oracle 在某些情况下可以避免排序操作(例如,如果在计划中使用合适的索引以正确顺序访问行),在这种情况下,Oracle 可能会找到允许合并的计划。
【解决方案3】:

试试这个:

SELECT field1, field2 , field3, field4, field5, field6, field7, to_char(rn) field8 from  
 (SELECT PM.POLICY_NO field1 
         ,PM.INSURED_CODE field2 
         ,PM.INSURED_NAME field3 
         ,TO_CHAR(PM.POLICY_EFFECTIVE_DATE,'DD/MM/YYYY') field4 
         ,TO_CHAR(PM.POLICY_EXPIRATION_DATE,'DD/MM/YYYY') field5 
         ,'' field6 
         ,'' field7 
         ,rownum as rn
   FROM POLICY_MAIN PM 
        inner join POLICY_ENDORSEMENT_MAIN PEM 
           on PM.POLICY_NO = PEM.POLICY_NO 
        inner join MASTER_UW_LOB_CLASS MAS 
           on PM.POLICY_LOB = MAS.UW_LOB_CODE 
  WHERE PM.POLICY_NO LIKE UPPER('%%') 
    AND PM.INSURED_CODE LIKE UPPER('%%') 
    AND PM.SOURCE_OF_BUSINESS LIKE UPPER('%%') 
    AND PM.POLICY_TYPE IS NULL 
    AND PM.POLICY_STATUS = 'POST' 
    AND MAS.UW_CLASS_CODE = 'AUTO'
    AND PEM.ENDORSEMENT_STATUS = 'POST'
    AND PEM.POLICY_ENDORSEMENT_NO = 
         (SELECT MAX(PEM2.POLICY_ENDORSEMENT_NO) 
            FROM POLICY_ENDORSEMENT_MAIN PEM2 
           WHERE PEM.POLICY_NO           = PEM2.POLICY_NO 
         ) 
  order by pm.policy_no ASC) 
WHERE rn BETWEEN 21 AND 40 

变化:

  1. 重组连接以使用 ANSI 语法区分连接和过滤器。
  2. LIKE UPPER('AUTO') 更改为= 'AUTO'
  3. 删除了不必要的嵌套级别。
  4. 将顺序更改为使用表达式与位置记法
  5. 将过滤条件 PEM.ENDORSEMENT_STATUS = 'POST' 从相关子查询移至主查询,这可能会纠正错误的结果问题。
  6. 将分页条件更改为使用数字表达式而不是字符表达式,因为:

    select * from dual where '211' between '21' and '40';

    select * from dual where 211 between 21 and 40;

不要返回相同的结果。

【讨论】:

  • 别忘了,ROWNUM 是在排序之前应用的——所以这可能不会返回预期的结果;您可能确实需要额外的嵌套级别。
  • 感谢您的更改-尽管您的查询可能有效-我最关心的是尝试找出为什么我发布的查询从 9i 到 10g 的工作方式不同。由于它是一个现有的应用程序,其中包含与我发布的类似的数百个查询,理想情况下,我不想像您此时向所有人建议的那样进行重大更改,因为这将涉及大量的重新测试
【解决方案4】:

解释计划应该可以帮助您识别问题。正如托尼所说,任何将内部查询合并到外部查询都会破坏您的查询。条件 RONUM > 1 无条件适用的任何查询都将失败。

您正在构建的查询可能需要构建整个结果集,然后过滤掉页面的行。您可能需要考虑在内部查询中为所需的行构建一个键集,然后在外部查询中添加其他列。在 rownum 上选择查询的 CARDINALITY 提示可能会有所帮助。

尝试使用“rownum() over (order by 1) rn”来生成订单。 (我假设 order 有时不同于 1。)在第一个内部查询中添加“/*+ FIRST_ROWS(20) */”。 http://www.oracle.com/technology/oramag/oracle/07-jan/o17asktom.html 寻求更多帮助。

【讨论】:

  • @Bill - 我的内部查询“SELECT PM.POLICY_NO field1 PM......”是尝试首先构建整个结果集...外部选择只是一个额外的选择查询为了使用外部查询中的 rownum 过滤掉该结果集中的行” - 这与您的建议不同吗?
  • 另外,你说得对,解释计划通过指示正在创建一个额外的视图来帮助它,这似乎扰乱了我的 rownum 逻辑。我真正想知道的是为什么?这是因为这是从 9i 迁移的现有应用程序,其中相同的查询工作正常,并且由于涉及的测试工作,我不想更改所有这些查询。我不确定某些设置是否导致优化器工作方式不同,也许我可以通过更改这些设置来代替!我希望这能更好地解释我的困境!
  • 10g 自动生成统计信息。这些需要在 9i 中手动生成。如果您没有生成统计信息,则生成的计划可能与生成的统计信息有很大不同。一般来说,好的统计数据会产生更好的计划。但是,也有例外,您的情况可能就是其中之一。如果您仍然可以在 9i 环境中运行查询,那么比较这些计划会很有趣。
  • @Bill - 添加了来自 9i 环境的原始查询计划,以便与原始帖子中的编辑 4 进行比较。同一查询的 10g 查询计划是 Edit 2。如果比较 2,唯一的关键区别似乎是我的 9i 计划中步骤 5 的“过滤”步骤现在发生在我的 10g 计划的步骤 3 中,这使得rownum 的所有区别。你能解释一下为什么会发生这种情况吗?
  • 被删除的视图将小时和中间选择语句分开。这种分离是允许 rownum 范围选择工作的原因。没有它,你最终会选择“rownum >= 20 和 rownum
猜你喜欢
  • 2018-01-13
  • 1970-01-01
  • 1970-01-01
  • 2020-05-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-26
相关资源
最近更新 更多