![]() | ||||||||||||||||||||||||||||||
![]() |
![]() |
|
|
|||||||||||||||||||||||||||
![]() |
|
Использование нескольких индексов для выполнения запросаИсточник: ln
В этом выпуске мы рассмотрим некоторые особенности использования индексов в Oracle. Максимальное количество индексов для запросаКогда в запросе есть два и более условия с проверкой на равенство, можно использовать несколько индексов. Oracle будет "объединять" ( сливать , merge) индексы во время выполнения, возвращая строки, которые найдены по обоим индексам. Например, при выполнении запроса: SELECT ename FROM emp WHERE deptno=20 AND job='manager' может быть выполнено слияние следующих индексов:
А как реально сервер осуществляет слияние индексов? Что лучше: конкатенированный индекс по столбцам job и deptno или отдельные индексы по столбцам job и deptno, которые могут быть объединены? Правда ли, что Oracle использует для запроса не более 5 индексов? Сколько максимум индексов по таблице можно создать? Ответ Тома КайтаИндексы объединяются именно так, как вы и подумали. Условие deptno=20 будет генерировать один список идентификаторов строк-кандидатов, а условие job='manager' - другой. Эти списки будут объединяться, и фактически выбраны из таблицы будут только строки, входящие в оба списка. А вот ответ на вопрос, что лучше (как всегда) зависит от многих обстоятельств. Бывают случаи, благоприятные для обоих вариантов. В рассмотренном выше примере, конкатенированный индекс по deptno, job, скорее всего, будет лучше (работы меньше). Однако, если в таблице EMP есть 50 столбцов, а в условии могут упоминаться любые ДВА из них? Вы хотите создавать индекс для каждого сочетания (я - нет)? При работе с индексами на основе битовых карт (bitmap indexes), использовать конкатенированный индекс почти никогда нет смысла. Практически всегда индексируются отдельные столбцы. Что касается ограничения "5", - мы будем использовать столько индексов, сколько нужно. Например: ops$tkyte@ORA817.US.ORACLE.COM> create table t ( a int, b int, c int, d int, e int, f int, g int, h int, i int ); Table created. ops$tkyte@ORA817.US.ORACLE.COM> create bitmap index t_a on t(a); Index created. ops$tkyte@ORA817.US.ORACLE.COM> create bitmap index t_b on t(b); Index created. ops$tkyte@ORA817.US.ORACLE.COM> create bitmap index t_c on t(c); Index created. ops$tkyte@ORA817.US.ORACLE.COM> create bitmap index t_d on t(d); Index created. ops$tkyte@ORA817.US.ORACLE.COM> create bitmap index t_e on t(e); Index created. ops$tkyte@ORA817.US.ORACLE.COM> create bitmap index t_f on t(f); Index created. ops$tkyte@ORA817.US.ORACLE.COM> create bitmap index t_g on t(g); Index created. ops$tkyte@ORA817.US.ORACLE.COM> create bitmap index t_h on t(h); Index created. ops$tkyte@ORA817.US.ORACLE.COM> create bitmap index t_i on t(i); Index created. ops$tkyte@ORA817.US.ORACLE.COM> exec dbms_stats.set_table_stats( user, 'T', numrows=>10000000, numblks => 10000000 ); PL/SQL procedure successfully completed. ops$tkyte@ORA817.US.ORACLE.COM> set autotrace on explain ops$tkyte@ORA817.US.ORACLE.COM> select count(*) 2 from t 3 where a = 1 4 and b = 2 5 and c = 3 6 and d = 4 7 and e = 5 8 and f = 6 9 and g = 7 10 and h = 8 11 and i = 9 12 / COUNT(*) ---------- 0 Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=10 Card=1 Bytes=100) 1 0 SORT (AGGREGATE) 2 1 BITMAP CONVERSION (COUNT) 3 2 BITMAP AND 4 3 BITMAP INDEX (SINGLE VALUE) OF 'T_A' 5 3 BITMAP INDEX (SINGLE VALUE) OF 'T_B' 6 3 BITMAP INDEX (SINGLE VALUE) OF 'T_C' 7 3 BITMAP INDEX (SINGLE VALUE) OF 'T_D' 8 3 BITMAP INDEX (SINGLE VALUE) OF 'T_E' 9 3 BITMAP INDEX (SINGLE VALUE) OF 'T_F' 10 3 BITMAP INDEX (SINGLE VALUE) OF 'T_G' 11 3 BITMAP INDEX (SINGLE VALUE) OF 'T_H' 12 3 BITMAP INDEX (SINGLE VALUE) OF 'T_I' Даже если используются обычные индексы, можно будет их соединить: ops$tkyte@ORA817.US.ORACLE.COM> select /*+ index_join( t ) */ count(*) 2 from t 3 where a = 1 4 and b = 2 5 and c = 3 6 and d = 4 7 and e = 5 8 and f = 6 9 and g = 7 10 and h = 8 11 and i = 9 12 / COUNT(*) ---------- 0 Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=32 Card=1 Bytes=100) 1 0 SORT (AGGREGATE) 2 1 VIEW OF 'index$_join$_001' (Cost=32 Card=1 Bytes=100) 3 2 HASH JOIN 4 3 HASH JOIN 5 4 HASH JOIN 6 5 HASH JOIN 7 6 HASH JOIN 8 7 HASH JOIN 9 8 HASH JOIN 10 9 HASH JOIN 11 10 INDEX (RANGE SCAN) OF 'T_A' (NON-UNIQUE) (Cost=11 Card=1 Bytes=100) 12 10 INDEX (RANGE SCAN) OF 'T_B' (NON-UNIQUE) (Cost=11 Card=1 Bytes=100) 13 9 INDEX (RANGE SCAN) OF 'T_C' (NON-UNIQUE) (Cost=11 Card=1 Bytes=100) 14 8 INDEX (RANGE SCAN) OF 'T_D' (NON-UNIQUE) (Cost=11 Card=1 Bytes=100) 15 7 INDEX (RANGE SCAN) OF 'T_E' (NON-UNIQUE) (Cost=11 Card=1 Bytes=100) 16 6 INDEX (RANGE SCAN) OF 'T_F' (NON-UNIQUE) (Cost=11 Card=1 Bytes=100) 17 5 INDEX (RANGE SCAN) OF 'T_G' (NON-UNIQUE) (Cost=11 Card=1 Bytes=100) 18 4 INDEX (RANGE SCAN) OF 'T_H' (NON-UNIQUE) (Cost=11 Card=1 Bytes=100) 19 3 INDEX (RANGE SCAN) OF 'T_I' (NON-UNIQUE) (Cost=11 Card=1 Bytes=100) Использование нескольких индексов...Я тут борюсь с использованием нескольких индексов для Index Join... У меня есть два индекса на основе b-дерева по таблице, и я хочу выбирать данные путем соединения этих двух индексов, а не полным просмотром таблицы. Эти два индекса содержат все столбцы, которые выбираются в запросе. Я пытался использовать подсказку /*+ INDEX_JOIN(TAB1 IND1 IND2) */, но она не работает. Не могли бы вы объяснить, как использовать Index Join для индексов на основе B-деревьев? Какие параметры инициализации надо установить, чтобы эта возможность использовалась? Ответ Тома КайтаПримеры - всегда нужны примеры того, что именно пытались делать, чтобы объяснить, почему что-то произошло или не произошло... Причин может быть множество. Среди наиболее вероятных - следующие:
ops$tkyte@ORA817DEV.US.ORACLE.COM> @desc big_table Datatypes for Table big_table Data Data Column Name Type Length Nullable ------------------------------ -------------------- ----------- -------- OWNER VARCHAR2 30 not null OBJECT_NAME VARCHAR2 30 not null SUBOBJECT_NAME VARCHAR2 30 null OBJECT_ID NUMBER not null DATA_OBJECT_ID NUMBER null OBJECT_TYPE VARCHAR2 18 null CREATED DATE 7 not null LAST_DDL_TIME DATE 7 not null TIMESTAMP VARCHAR2 19 null STATUS VARCHAR2 7 null TEMPORARY VARCHAR2 1 null GENERATED VARCHAR2 1 null SECONDARY VARCHAR2 1 null Indexes on big_table Index Is Name Unique COLUMNS ------------------------------ ------ -------------------------------- BIG_TABLE_IDX1 No OBJECT_NAME BIG_TABLE_IDX2 No OBJECT_ID Для этой таблицы из 1000000 строк я запросто добиваюсь соединения индексов: ops$tkyte@ORA817DEV.US.ORACLE.COM> select /*+ index_join( big_table big_table_idx1 big_table_idx2 ) */ object_name, object_id 2 from big_table 3 where object_name like 'ABCDEF%' 4 and object_id between 1000 and 1500 5 / no rows selected Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=83 Card=138 Bytes=4140) 1 0 VIEW OF 'index$_join$_001' (Cost=83 Card=138 Bytes=4140) 2 1 HASH JOIN 3 2 INDEX (RANGE SCAN) OF 'BIG_TABLE_IDX2' (NON-UNIQUE) (Cost=81 Card=138 Bytes=4140) 4 2 INDEX (RANGE SCAN) OF 'BIG_TABLE_IDX1' (NON-UNIQUE) (Cost=81 Card=138 Bytes=4140) Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 73 consistent gets 0 physical reads 0 redo size 272 bytes sent via SQL*Net to client 319 bytes received via SQL*Net from client 1 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 0 rows processed Мне пришлось задать подсказку, поскольку это ОШИБОЧНЫЙ план: ops$tkyte@ORA817DEV.US.ORACLE.COM> select object_name, object_id 2 from big_table 3 where object_name like 'ABCDEF%' 4 and object_id between 1000 and 1500 5 / no rows selected Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=431 Card=138 Bytes=4140) 1 0 TABLE ACCESS (BY INDEX ROWID) OF 'BIG_TABLE' (Cost=431 Card=138 Bytes=4140) 2 1 INDEX (RANGE SCAN) OF 'BIG_TABLE_IDX1' (NON-UNIQUE) (Cost=52 Card=138) Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 3 consistent gets 0 physical reads 0 redo size 272 bytes sent via SQL*Net to client 319 bytes received via SQL*Net from client 1 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 0 rows processed Никаких параметров инициализации задавать не нужно. Подсказка index_join приводит к использованию только одного из индексов...У меня есть запрос с подсказкой index_join: SELECT /*+index_join(ho idx1 idx2)*/ sum( nvl(mw,0) ) FROM ho WHERE fko = :b4 AND fks = :b3 AND hdate >= :b2 AND hdate < :b1 Получаемый в результате план выполнения на oracle 9.2.0.3.0 (AIX - 64BIT) показывает, что выполняется index_join, но используется только один из указанных индексов, и соединяется он с неким третим индексом. Проблема в том, что этот третий индекс не секционирован, поэтому запрос работает дольше. / Rows Row Source Operation /--------- --------------------------------------------------- / 345 SORT AGGREGATE (cr=6409065 pr=6730699 pw=340953 time=7952.32) / 252288 .FILTER (cr=6409065 pr=6730699 pw=340953 time=7951.67) / 252288 ..VIEW (cr=6409065 pr=6730699 pw=340953 time=7951.20) / 252288 ...HASH JOIN (cr=6409065 pr=6730699 pw=340953 time=7950.17) /154842493 ....PARTITION RANGE ITERATOR PARTITION: KEY KEY (cr=264615 pr=264615 pw=0 time=534.27) /154842493 .....INDEX RANGE SCAN idx2 PARTITION: KEY KEY (object id 42026 ) (cr=264615 pr=264615 pw=0 time=316.62) / 5228609 ....INDEX FAST FULL SCAN idx3 (object id 36664 ) (cr=6144450 pr=6125131 pw=0 time=6704.00) Меня интересует, почему Oracle выбрал только один из двух индексов, указанных в подсказке index_join? Вот определения таблицы, представления и индекса: CREATE TABLE ho_t (hid NUMBER(9,0) NOT NULL, hdate DATE NOT NULL, fko NUMBER(9,0) NOT NULL, fks NUMBER(9,0) NOT NULL, df NUMBER(1,0) NOT NULL, mw NUMBER(13,3) --... еще 25 столбцов ) PARTITION BY RANGE (hdate) ( PARTITION p_2_2 VALUES LESS THAN (TO_DATE(' 2000-07-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) --... еще 15 секций ) / CREATE OR REPLACE VIEW ho ( hid, hdate, fko, fks, df, mw --.. еще 25 столбцов ) AS select hid, hdate, fko, fks, df, mw --... еще 25 столбцов from ho_t / CREATE UNIQUE INDEX idx1 ON ho_t ( hdate ASC, fks ASC, df ASC, fko ASC ) LOCAL ( PARTITION p_2_2 --... еще 15 секций ) / CREATE INDEX idx2 ON ho_t ( hdate ASC, fko ASC, mw ) LOCAL ( PARTITION p_2_2 --... еще 15 секций ) / CREATE INDEX idx3 ON ho_t ( fko ASC, fks ASC ) / ALTER TABLE ho_t ADD CONSTRAINT pk_ho_t PRIMARY KEY (hid) USING INDEX / ALTER TABLE ho_t ADD CONSTRAINT fk_h FOREIGN KEY (fko) REFERENCES R.s (oi) ON DELETE SET NULL / Ответ Тома КайтаПомогите мне "поломать" следующий пример: drop table ho_t; CREATE TABLE ho_t (hid NUMBER(9,0) NOT NULL, hdate DATE NOT NULL, fko NUMBER(9,0) NOT NULL, fks NUMBER(9,0) NOT NULL, df NUMBER(1,0) NOT NULL, mw NUMBER(13,3) ) partition by range(hdate) ( partition p1 values less than (TO_DATE(' 2000-07-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')), partition p2 values less than (TO_DATE(' 2001-07-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) ) / create or replace view ho as select * from ho_t / CREATE UNIQUE INDEX idx1 ON ho_t ( hdate ASC, fks ASC, df ASC, fko ASC ) local / CREATE INDEX idx2 ON ho_t ( hdate ASC, fko ASC, mw ) local / CREATE INDEX idx3 ON ho_t ( fko ASC, fks ASC ) / variable b1 varchar2(25); variable b2 varchar2(25); variable b3 varchar2(25); variable b4 varchar2(25); set linesize 121 delete from plan_table; explain plan for SELECT /*+index_join(ho idx1 idx2)*/ sum( nvl(mw,0) ) FROM ho WHERE fko = to_number(:b4) AND fks = to_number(:b3) AND hdate >= to_date(:b2) AND hdate < to_date(:b1) / select * from table(dbms_xplan.display); Комментарий читателя от 6 мая 2004 годаЧтобы "поломать" ваш сценарий, мне пришлось бы скопировать данные из исходной таблицы (более 11 миллионов строк) и посмотреть, будет ли ваша таблица вести себя так же, как и исходная. Я надеялся, что вы уже сталкивались с этой проблемой, например, с недокументированными ограничениями подсказки index_join, вот почему я и послал определения таблицы, индексов и представления... Ответ Тома КайтаНет, используйте пакет dbms_stats для установки статистической информации для таблицы и т.п., чтобы "мой" план стал "вашим". Если соединение индексов можно использовать, оно должно было использоваться. Должно быть нечто, что сделает этот тестовый пример "более походим на реальный", чтобы получить ваш результат. Ссылки по теме
|
|