Oracle 10G - 使用rownum的查询在从9i迁移后停止工作

时间:2010-08-03 07:42:59

标签: oracle10g rownum

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

我的Oracle 10G数据库的详细信息如下: -

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 ='发布'

  2. 如果我从我的查询中评论此行并且其他所有内容与原始内容保持不变,则查询也可以正常运行

    按1 ASC排序

  3. 与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
    

    子句,然后按预期检索结果。 没有订单的此查询的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之后,不工作的那个有以下两个额外步骤,因为这些步骤在没有顺序的查询中不存在 - 这工作正常。

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

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

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

    为什么这应该阻止rownum逻辑工作是我仍然想要掌握的。

    任何帮助表示赞赏!!

    编辑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)
    

4 个答案:

答案 0 :(得分:2)

正如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

答案 1 :(得分:1)

听起来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?)

答案 2 :(得分:1)

试试这个:

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. 更改分页条件以使用数字表达式而不是字符1,因为:

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

    select * from dual where 211 between 21 and 40;

  7. 不要返回相同的结果。

答案 3 :(得分:0)

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

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

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