Рассылка закрыта
При закрытии подписчики были переданы в рассылку "СУБД 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 |
Отписаться |
| В избранное | ||
