Рассылка закрыта
При закрытии подписчики были переданы в рассылку "СУБД Oracle "с нуля"" на которую и рекомендуем вам подписаться.
Вы можете найти рассылки сходной тематики в Каталоге рассылок.
Открыто о СУБД Oracle на русском : особенности использования представлений...
Информационный Канал Subscribe.Ru |
Выпуск 83
Уважаемые подписчики рассылки!
Этот выпуск посвящен особенностям использования представлений, в частности,
содержащих аналитические функции. По мотивам интересного, с моей точки
зрения, обсуждения на сайте Тома Кайта.
Проталкивание условия в представлениях, содержащих аналитические функции
У меня в базе данных есть представление, содержащее аналитическую функцию. Аналитическая функция проходит окном по набору строк таблицы и возвращает их подмножество. Начальное тестирование использования этого представления дало весьма многообещающие результаты, поскольку при использовании аналитической функции серверу для получения ответа потребовалось выполнить НАМНОГО меньше действий, чем при соединении таблицы с самой собой (ранее использовался такой подход). При этом использовались запросы к представлению, отбирающие данные по тому же столбцу, по которому выполнялось секционирование таблицы в аналитической функции. Однако, когда в запросе к представлению я задал условие по другому столбцу в конструкции WHERE, производительность существенно снизилась. Мое исследование показало, что условие не было протолкнуто в представление, и в результате пришлось читать всю таблицу, разбивать на секции и запоминать, прежде чем было применено условие.
Нет ли способа принудительно протолкнуть условие в представление? Я пытался использовать подсказки вроде PUSH_PRED, но так и не смог ничего добиться. Нет ли другого способа построить представление для получения необходимых мне данных?
Ниже представлен длинный фрагмент сеанса SQL*Plus, в котором описаны подробности проведенного исследования.
SQL>drop table t; Table dropped. Elapsed: 00:00:00.41 SQL>create table t( Group_id VARCHAR2(32), 2 Group_Key NUMBER, 3 col2 NUMBER ); Table created. Elapsed: 00:00:00.40 SQL>insert into t 2 select object_name, 3 ROW_NUMBER() OVER( partition by object_name 4 order by object_id ), 5 object_id 6 from all_objects 7 where rownum<=25000 8 / 24862 rows created. Elapsed: 00:00:10.05 SQL>insert into t 2 select Group_id || '1', 3 Group_Key, 4 col2 * -1 5 from t 6 / 24862 rows created. Elapsed: 00:00:02.03 SQL>commit; Commit complete. Elapsed: 00:00:00.90 SQL>-- Создаем индексы по 2 столбцам. SQL>-- Один - по столбцу, используемому для секционирования в аналитической функции SQL>-- Другой - по еще одному столбцу таблицы SQL>create index IDX_T_GRPID on T(GROUP_ID); Index created. Elapsed: 00:00:08.63 SQL>create index IDX_T_COL2 on T(COL2); Index created. Elapsed: 00:00:09.05 SQL>-- Вычисляем статистическую информацию для CBO SQL>analyze table t compute statistics; Table analyzed. Elapsed: 00:00:14.51 SQL>-- Создаем пакет, который будет использоваться для отслеживания SQL>-- количества строк, обработанных аналитической функцией. SQL>-- Он также будет использоваться, чтобы отбросить некоторые строки, SQL>-- обработанные аналитической функцией. SQL>create or replace package test_anafunc is 2 function test_func( x NUMBER ) return number; 3 4 function get_Count return number; 5 procedure reset_count; 6 end; 7 / Package created. Elapsed: 00:00:00.70 SQL>create or replace package body test_anafunc is 2 3 p_Count NUMBER(38); 4 5 function test_func( x NUMBER ) return number is 6 begin 7 p_Count := p_Count + 1; 8 9 if( x > 1 ) then 10 return 1; 11 end if; 12 13 return 0; 14 end test_func; 15 16 function get_Count return number is 17 begin 18 return p_Count; 19 end get_Count; 20 21 procedure reset_count is 22 begin 23 p_Count := 0; 24 end reset_Count; 25 26 begin 27 p_Count := 0; 28 end; 29 / Package body created. Elapsed: 00:00:00.70 SQL>-- Создаем представление, содержащее аналитическую функцию. SQL>-- Оно имитирует представление, которое будет открыто приложением SQL>-- конченым пользователям. SQL>create or replace view test_view as 2 select a.group_id, a.group_key, a.col2 3 from (select t.group_id, t.group_key, t.col2, 4 ROW_NUMBER() OVER( PARTITION BY GROUP_ID 5 ORDER BY GROUP_KEY ASC NULLS LAST ) RNUM 6 from t 7 where test_anafunc.test_func(GROUP_KEY) = 1 ) a 8 where a.RNUM = 1 9 / View created. Elapsed: 00:00:00.90 SQL>-- Давайте посмотрим, как выполняются запросы к этому представлению. SQL> SQL>-- Запрашиваем представление по group_id. SQL>-- Выбираем такое значение из таблицы t, для которого есть более одной строки. SQL>-- Мы должны увидеть вторую строку группы (Group_key=2). SQL>-- План выполнения должен показывать использование индекса. SQL>-- Вызов test_anafunc.get_Count должен вернуть количество строк для SQL>-- соответствующего значения group_id. SQL>-- Это показывает, что условие протолкнуто во вложенное представление. SQL>execute test_anafunc.reset_Count PL/SQL procedure successfully completed. Elapsed: 00:00:00.10 SQL>set autotrace on explain statistics SQL>select * from test_View where group_id = 'TRACE'; GROUP_ID GROUP_KEY COL2 -------------------------------- ---------- ---------- TRACE 2 7942 Elapsed: 00:00:00.21 Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=5 Card=1 Bytes=57) 1 0 VIEW (Cost=5 Card=1 Bytes=57) 2 1 WINDOW (SORT) 3 2 TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=3 Card=1 Bytes=27) 4 3 INDEX (RANGE SCAN) OF 'IDX_T_GRPID' (NON-UNIQUE) (Cost=1 Card=1) Statistics ---------------------------------------------------------- 42 recursive calls 0 db block gets 9 consistent gets 0 physical reads 0 redo size 486 bytes sent via SQL*Net to client 425 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 3 sorts (memory) 0 sorts (disk) 1 rows processed SQL>set autotrace off SQL>select test_anafunc.get_Count from sys.dual; GET_COUNT ---------- 7 Elapsed: 00:00:00.20 SQL>-- Запрос по столбцу, не использованному для секционирования. SQL>-- Выбираем object_id, существующий в таблице t в столбце col2. SQL>-- Должны получить одну строку. SQL>-- Однако, план выполнения не покажет использования индекса. Вместо SQL>-- этого вы увидите полный просмотр таблицы. SQL>-- Вызов test_anafunc.get_Count вернет значение, равное общему SQL>-- количеству строк. SQL>-- Это показывает, что условие НЕ протолкнуто во вложенное представление SQL>-- и что мы обрабатываем всю таблицу, а не только те строки, SQL>-- которые удовлетворяют условиям конструкции where. SQL>execute test_anafunc.reset_Count PL/SQL procedure successfully completed. Elapsed: 00:00:00.10 SQL>/ GET_COUNT ---------- 0 Elapsed: 00:00:00.10 SQL>set autotrace on explain statistics SQL>select * from test_view where col2 = 10816; GROUP_ID GROUP_KEY COL2 -------------------------------- ---------- ---------- ADDRESSLOCATION_SDOGEOM 2 10816 Elapsed: 00:00:05.58 Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=40 Card=498 Bytes=28386) 1 0 VIEW (Cost=40 Card=498 Bytes=28386) 2 1 WINDOW (SORT) 3 2 TABLE ACCESS (FULL) OF 'T' (Cost=36 Card=498 Bytes=13446) Statistics ---------------------------------------------------------- 50 recursive calls 11 db block gets 242 consistent gets 251 physical reads 0 redo size 505 bytes sent via SQL*Net to client 425 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 2 sorts (memory) 1 sorts (disk) 1 rows processed SQL>set autotrace off SQL>select test_anafunc.get_Count from sys.dual; GET_COUNT ---------- 49724 Elapsed: 00:00:00.10 SQL>-- Сравните это с тем, что я хотел бы получить для запроса по col2. SQL>execute test_anafunc.reset_Count PL/SQL procedure successfully completed. Elapsed: 00:00:00.10 SQL>/ GET_COUNT ---------- 0 Elapsed: 00:00:00.10 SQL>set autotrace on explain statistics SQL>select a.group_id, a.group_key, a.col2 2 from (select t.group_id, t.group_key, t.col2, 3 ROW_NUMBER() OVER( PARTITION BY GROUP_ID 4 ORDER BY GROUP_KEY ASC NULLS LAST ) RNUM 5 from t 6 where test_anafunc.test_func(GROUP_KEY) = 1 7 and col2 = 10816 ) a 8 where a.RNUM = 1 9 / GROUP_ID GROUP_KEY COL2 -------------------------------- ---------- ---------- ADDRESSLOCATION_SDOGEOM 2 10816 Elapsed: 00:00:00.11 Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=4 Card=1 Bytes=57) 1 0 VIEW (Cost=4 Card=1 Bytes=57) 2 1 WINDOW (SORT) 3 2 TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=2 Card=1 Bytes=27) 4 3 INDEX (RANGE SCAN) OF 'IDX_T_COL2' (NON-UNIQUE) (Cost=1 Card=1) Statistics ---------------------------------------------------------- 35 recursive calls 0 db block gets 7 consistent gets 0 physical reads 0 redo size 505 bytes sent via SQL*Net to client 425 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 3 sorts (memory) 0 sorts (disk) 1 rows processed SQL>set autotrace off SQL>select test_anafunc.get_Count from sys.dual 2 / GET_COUNT ---------- 1 Elapsed: 00:00:00.10
Ответ Тома Кайта
Этого нельзя сделать — при этом ИЗМЕНИТСЯ ответ. Вы думаете, что ваши два запроса эквивалентны, но это НЕ ТАК.
Есть ОГРОМНАЯ разница между
select analytic function from t where <условие>
и
select * from ( select analytic function from t ) where <условие>
Они даже и близко НЕ СРАВНИМЫ. Вообще.
Этот запрос говорит:
SQL>select a.group_id, a.group_key, a.col2 2 from (select t.group_id, t.group_key, t.col2, 3 ROW_NUMBER() OVER( PARTITION BY GROUP_ID 4 ORDER BY GROUP_KEY ASC NULLS LAST ) RNUM 5 from t 6 where test_anafunc.test_func(GROUP_KEY) = 1 7 and col2 = 10816 ) a 8 where a.RNUM = 1 9 /
Выбери строки из T, пропуская строки с group_key 1 (это делает test_func), и выбирая те, в которых col2 = 10816.
Затем, для всех таких строк - секционируй по group_id и отсортируй по group_key, присваивая row_number.
Затем, оставь только первую строку.
А этот запрос:
select * from (select a.group_id, a.group_key, a.col2 from (select t.group_id, t.group_key, t.col2, ROW_NUMBER() OVER( PARTITION BY GROUP_ID ORDER BY GROUP_KEY ASC NULLS LAST ) RNUM from t where test_anafunc.test_func(GROUP_KEY) = 1 ) a where col2 = 10816 and a.RNUM = 1 /
говорит:
Найди все строки, в которых group_key не равен 1. Секционируй их по group_id. Отсортируй по group_key и присвой row_number.
Теперь, когда это сделано, оставь только первую строку ПРИ УСЛОВИИ, что в ней col2 = 10816.
Вот доказательство того, что эти запросы отличаются — они ВЕСЬМА, ВООБЩЕ и полностью отличаются:
ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table t 2 / Table dropped. ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t( Group_id VARCHAR2(32), 2 Group_Key NUMBER, 3 col2 NUMBER ) 4 / Table created. ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 'x', 2, 10815 ); 1 row created. ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 'x', 3, 10816 ); 1 row created. ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace view test_view as 2 select a.group_id, a.group_key, a.col2 3 from (select t.group_id, t.group_key, t.col2, 4 ROW_NUMBER() OVER( PARTITION BY GROUP_ID 5 ORDER BY GROUP_KEY ASC NULLS LAST ) RNUM 6 from t 7 where test_anafunc.test_func(GROUP_KEY) = 1 ) a 8 where a.RNUM = 1 9 / View created. ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from test_view where col2 = 10816; no rows selected ops$tkyte@ORA817DEV.US.ORACLE.COM> select * 2 from ( 3 select a.group_id, a.group_key, a.col2 4 from (select t.group_id, t.group_key, t.col2, 5 ROW_NUMBER() OVER( PARTITION BY GROUP_ID 6 ORDER BY GROUP_KEY ASC NULLS LAST ) RNUM 7 from t 8 where test_anafunc.test_func(GROUP_KEY) = 1 ) a 9 where a.RNUM = 1 10 ) 11 where col2 = 10816 12 / no rows selected ops$tkyte@ORA817DEV.US.ORACLE.COM> select a.group_id, a.group_key, a.col2 2 from (select t.group_id, t.group_key, t.col2, 3 ROW_NUMBER() OVER( PARTITION BY GROUP_ID 4 ORDER BY GROUP_KEY ASC NULLS LAST ) RNUM 5 from t 6 where test_anafunc.test_func(GROUP_KEY) = 1 7 and col2 = 10816 ) a 8 where a.RNUM = 1 9 / GROUP_ID GROUP_KEY COL2 -------------------------------- ---------- ---------- x 3 10816
Делаю ли я запрос к представлению или запрос с вложенным представлением — данных нет (по определению — такой запрос не должен возвращать данных).
Делаем так, как вы хотите — и получаем строку. Но это потому, что задавался абсолютно другой вопрос!
Достаточно ясно :)
Я подозревал, что причина такого поведения — изменение ответа. Спасибо за хороший пример, который это доказал.
Не могли бы вы предложить другой подход для получения того, что мне нужно? Важно, чтобы результаты каждого запроса к таблице обрабатывались аналитической функцией. Т.е. какие бы условия пользователь/приложение не задавали бы для поиска подмножества строк, надо прогнать их через функцию test_func для сокращения количества, а затем, для оставшегося набора строк, надо применить аналитическую функцию с окном, для выбора одной из них (RNUM=1).
Моя проблема лишь в том, что я не могу сформулировать корректный SQL-оператор для такого требования? Или этого просто нельзя сделать, используя представления с аналитическими функциями?
Я вынужден использовать представление, потому что аналитические функции нельзя использовать в конструкции WHERE. Но если бы и не это, я хотел бы, чтобы из приложения запрос шел к представлению, чтобы оно "видело" только часть столбцов таблицы.
Я знаю другие способы решения проблемы, но они не настолько просты, как использование аналитических функций (если только мне удастся их применить). :)
Ответ Тома Кайта
В таком случае, ваши требования нельзя удовлетворить с помощью представления.
Условия всегда будут применяться ПОСЛЕ вычисления аналитических функций в представлении, они должны проверяться именно так. Потому, что:
select analytic from t where условие
СИЛЬНО и ВСЕГДА отличается от
select * from ( select analytic from t ) where условие
Проблема, по сути, в том, что из одного нельзя получить другое.
Высказанная вами причина использования представления не связана с требованиями. МОЖНО использовать результат аналитической функции в условии (вы ИСПОЛЬЗУЕТЕ — rnum = 1)!!! Это просто надо делать ПОСЛЕ, а не ПО ХОДУ.
Это аналогично функциям агрегирования. Нельзя сказать: where count(*) = 1, надо говорить HAVING count(*) = 1. Условие проверяется ПОСЛЕ построения результирующего множества. С аналитическими функциями — то же самое.
Я уверен, что вы можете использовать аналитические функции (я знаю это).
Я также знаю, что вам не удастся использовать представление при указанном списке "обязательных требований".
Как заставить работать no_push_pred?
У меня есть запрос:
select ename, dname from (select * from emp where deptno = 10) a, (select * from dept where deptno = 10) b where a.empno = 7934; Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=3 Card=1 Bytes=21) 1 0 NESTED LOOPS (Cost=3 Card=1 Bytes=21) 2 1 TABLE ACCESS (BY INDEX ROWID) OF 'EMP' (Cost=1 Card=1 Bytes=10) 3 2 INDEX (UNIQUE SCAN) OF 'EMP_PK' (UNIQUE) 4 1 TABLE ACCESS (FULL) OF 'DEPT' (Cost=2 Card=1 Bytes=11)
Я думаю, что происходит проталкивание условия (where empno... ) в представление, поэтому я изменил запрос следующим образом:
select /*+ NO_PUSH_PRED(a) */ ename, dname from (select * from emp where deptno = 10) a, (select * from dept where deptno = 10) b where a.empno = 7934;
но получил тот же план выполнения, тогда как ожидал два полных просмотра таблиц emp и dept...
Как работает эта подсказка?
Ответ Тома Кайта
Она работает в случае соединения. А это не соединение. Вы можете использовать no_merge для получения альтернативного (но безумного) плана — но, по сути, CBO должен иметь возможность перемещать это условие.
Вы можете получить желаемый план, использовав фокус с ROWNUM:
scott@ORA920> explain plan for 2 select ename, dname 3 from (select * from emp where deptno = 10 and rownum > 0 ) a, 4 (select * from dept where deptno = 10) b 5 where a.empno = 7934 6 / Explained. scott@ORA920> select * from table(dbms_xplan.display); PLAN_TABLE_OUTPUT --------------------------------------------------------------------------------- --------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost | --------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | | | 1 | NESTED LOOPS | | | | | |* 2 | TABLE ACCESS FULL | DEPT | | | | |* 3 | VIEW | | | | | | 4 | COUNT | | | | | |* 5 | FILTER | | | | | |* 6 | TABLE ACCESS FULL| EMP | | | | --------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter("DEPT"."DEPTNO"=10) 3 - filter("A"."EMPNO"=7934) 5 - filter(ROWNUM>0) 6 - filter("EMP"."DEPTNO"=10) Note: rule based optimization 22 rows selected.
но при этом уместно спросить: "Зачем"?
Как заставить работать NO_PUSH_PRED?
У меня есть таблица с миллионами "хороших" строк и несколькими "мусорными". Я пытаюсь отфильтровать плохие записи по представлению даты в VARCHAR2 так, чтобы можно было преобразовать в дату только хорошие записи. Я просто использовал:
SELECT /*+ NO_PUSH_PRED (a) */ some_columns FROM ( SELECT some_columns FROM some_table WHERE SUBSTR(char_date_column,1,1) BETWEEN '0' AND '9' ) a , some_other_table b WHERE some_join_condition AND TO_DATE(a.char_date_column, 'YYYY-MM-DD') < TRUNC(SYSDATE, 'YEAR')
Однако условие проталкивается во вложенное представление "a", поскольку я получаю ошибки в функции TO_DATE для нескольких плохих записей. Если я выношу SUBSTR вне вложенного представления и использую ORDERED_PREDICATES, то все получается. Что посоветуете?
Ответ Тома Кайта
Испольуйте CASE:
where some_join_condition and case when substr( char_date_column,1,1) between '0' and '9' then to_date( .... ) else null end < trunc( .... );
Комметарий к исходному вопросу
Я понимаю, что вы ответили на исходный вопрос более двух лет назад, но мне интересно, помните ли вы, почему условие "group_id = 'TRACE'" (при указании которого индекс использовался) проталкивалось во вложенное представление. Связано ли это с тем, что поле group_id участвовало в аналитической функции, входящей в представление? Не будут ли результаты другими, если вложенное представление сначала строится, а потом к нему применяется условие "group_id = 'TRACE'"? Я собираюсь это проверить, но, к сожалению, сейчас доступа к базе данных у меня нет.
Ответ Тома Кайта
Да, поскольку мы указали столбец group_id в конструкции PARTIONED, условия по group_id можно "безопасно" проталкивать в запрос.
PUSH_PRED и OUTER JOIN
Может ли условие быть протолкнуто в подзапрос, соединяемый с помощью LEFT OUTER JOIN? Например:
SELECT /*+ PUSH_PRED */ FROM SMALL LEFT JOIN BIG ON SMALL.ID = BIG.ID WHERE SMALL.ID = 9
Будет ли условие "ID = 9" протолкнуто в подзапрос?
Ответ Тома Кайта
По возможности, да.
... (Много не особо интересных вопросов пропущено - прим. В.К.)Фокус с rownum>=0
У меня есть запрос, имеющий удовлетворительную производительность.
Я добавляю к нему условие:
and col1 not like '%something%'
и план запроса полностью меняется, со снижением производительности!
Если я добавлю 'rownum>=0' к существующему запросу, а перед ним еще и мое новое условие, все работает прекрасно. rownum приводит к выполнению внутреннего запроса (с удовлетворительной производительностью) и фильтр применяется только к полученным в результате строкам.
Вопрос: разумно ли добавлять это условие 'rownum>=0' в само представление? Если представление соединяется с другой таблицей, я думаю, это не позволит CBO сгенерировать оптимальный план, поскольку потребует материализовать (построить) представление?
Нет ли других способов добиться того же, что и фокус с 'rownum>=0'?
Ответ Тома Кайта
Добавление такого условия предотвращает слияние представлений и проталкивание услвоий.
Есть подсказки, которые тоже могут предотвратить слияние.
GROUP BY
Как насчет представления/запроса с конструкцией group by?
Когда я выполняю:
select * from ( select col1,col2,sum(col3) group by col1,col2 having count(*)>5 ) where col1='foo'
оказывается, что условие col1='foo' проталкивается во внутренний запрос.
Не должна ли конструкция 'having' или даже просто group by предотвращать проталкивание условия?
Ответ Тома Кайта
Нет, не в этом случае, поскольку вы группируете по столбцу col1? - упоминание столбца col1 в конструкции where "безопасно".
Безопасно?
Не уверен, что понял. Так будет условие по столбцу col1 протолкнуто в представление или нет?
Ответ Тома Кайта
Оно может и будет протолкнуто (вы же сами уже это написали?)
А я написал, что это "безопасно", поскольку по этому столбцу выполняется группировка. Происходи полное "слияние" – шага построения представления нет вообще:
ops$tkyte@ORA10GR1> create table t ( c1 int, c2 int, c3 int ); Table created. ops$tkyte@ORA10GR1> delete from plan_table; 3 rows deleted. ops$tkyte@ORA10GR1> explain plan for 2 select * 3 from ( 4 select c1, c2, sum(c3) from t 5 group by c1, c2 6 ) 7 where c1 = 5; Explained. ops$tkyte@ORA10GR1> select * from table(dbms_xplan.display); PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------- Plan hash value: 1028120241 --------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 39 | 3 (34)| 00:00:01 | | 1 | SORT GROUP BY | | 1 | 39 | 3 (34)| 00:00:01 | |* 2 | TABLE ACCESS FULL| T | 1 | 39 | 2 (0)| 00:00:01 | --------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter("C1"=5) Note ----- - dynamic sampling used for this statement 18 rows selected.
Статья в Oracle Magazine
В последнем выпуске Oracle Magazine вы объясняли слияние представлений (view merging) и проталкивание условий (predicate pushing). У меня, однако, есть вопрос по слиянию представлений. В одном из ваших примеров (по-моему, в третьем) используется следующее определение представления:
select * from whatever order by whatever
Вы написали, что из-за конструкции order by слияние этого представления невозможно. Как так? В чем отличие
select * from view where col = 'X'
от
select * from (select * from whatever where col = 'X' order by whatever )
И еще. Что вы имеет ввиду по "СЕМАНТИЧЕСКИМИ" изменениями при указании rownum (для проталкивания условий). Что такое семантика? Результирующее множество?
Ответ Тома Кайта
Мы не можем преобразовывать:
select * from t1, (select * from t2 order by x) t2 where t1.y = t2.y and t2.z = 5;
в:
select * from t1, t2 where t1.y = t2.y and t2.z = 5 order by t2.x;
но мы можем протолкнуть условие во вложенное представление:
select * from t1, (select * from t2 where z = 5 order by x) t2 where t1.y = t2.y;
Вот что я имел ввиду (учитывайте сложный общий случай, много таблиц, а не одна-две).
Семантика - это "смысл", "ответ". Изменяется ответ на вопрос... А это означает, что "так делать неправильно".
Запросы не будут семантически эквивалентны.
Почему нельзя выполнить слияние?
Я не понимаю, почему нельзя выполнить слияние, преобразовав
select * from t1, (select * from t2 order by x) t2 where t1.y = t2.y and t2.z = 5;в
select * from t1, t2 where t1.y = t2.y and t2.z = 5 order by t2.x;
Пусть у нас есть две следующих таблицы:
t2 x y z -- -- -- 3 2 5 1 2 5 2 3 4 4 1 8 t1 y w -- -- 2 5
Из-за чего семантическая ошибка?
Ответ Тома Кайта
Из-за конструкции order by — order by "по определению" предотвращает слияние.
Оригинал обсуждения этого вопроса можно найти здесь.
Copyright © 2005 Oracle Corporation
Прежние выпуски
Все вышедшие выпуски рассылки можно найти на сайте рассылки. Там же реализована возможность поиска материалов по ключевым словам (с помощью Google...)
В следующих выпусках
Предполагаю пока опубликовать в нескольких выпусках свои переводы глав 1, 2 и 19 книги Кайта "Expert one-on-one Oracle". Затем выход рассылки станет еще более нерегулярным :). Подошло время оглянуться на сделанное, как-то его проклассифицировать, отредактировать, снабдить единым глоссарием, а затем решить, что делать с этим проектом дальше.
С наилучшими пожеланиями,
В.К.
http://subscribe.ru/
http://subscribe.ru/feedback/ |
Подписан адрес: Код этой рассылки: comp.soft.db.openoracle |
Отписаться |
В избранное | ||