Oracle 10G - Запрос с использованием rownum перестал работать после перехода с 9i - PullRequest
3 голосов
/ 03 августа 2010

Мы только что перевели нашу БД с 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.

Я искал другие вопросы SO, связанные с rownum, но не смог найти ничего похожего.

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. Если я прокомментирую эту строку из моего запроса, запрос будет работать нормально.

    И 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

, затем результаты извлекаются, как и ожидалось. ПЛАН для этого запроса без заказа ниже

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 ]

2 голосов
/ 10 августа 2010

Как предложил Адам, подзапрос фильтрует результаты после применения сортировки и 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

Я также удалил TO_CHAR из ROWNUM - вы хотите использовать числа для сравнения этого диапазона.

EDIT

Попробуйте # 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 голос
/ 04 августа 2010

Попробуйте это:

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;

Не возвращать те же результаты.

1 голос
/ 03 августа 2010

Похоже, что 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;

(Кстати, почему TO_CHAR для ROWNMUM, когда вы рассматриваете его какв любом случае, в предложении WHERE?)

0 голосов
/ 09 августа 2010

Объясните план должен помочь вам определить проблему. Как заявил Тони, любое слияние внутреннего запроса с внешним запросом нарушит ваш запрос. Любые запросы, к которым безоговорочно применяется условие RONUM> 1, завершатся неудачей.

Для таких запросов, как вы, возможно, потребуется собрать весь набор результатов, а затем отфильтровать строки для страницы. Возможно, вы захотите создать набор ключей для нужных строк во внутреннем запросе, а затем добавить дополнительные столбцы во внешний запрос. Может помочь подсказка CARDINALITY при выборе запроса на rownum.

Попробуйте использовать «rownum () over (order by 1) rn» для генерации заказа). (Я предполагаю, что порядок иногда отличается от 1.) Добавьте "/ * + FIRST_ROWS (20) * /" к первому внутреннему запросу. http://www.oracle.com/technology/oramag/oracle/07-jan/o17asktom.html для получения дополнительной помощи.

...