powered by simpleCommunicator - 2.0.52     © 2025 Programmizd 02
Форумы / Oracle [игнор отключен] [закрыт для гостей] / Добавление объектного типа в запрос - отрубает использование функциональных индексов
6 сообщений из 6, страница 1 из 1
Добавление объектного типа в запрос - отрубает использование функциональных индексов
    #39946062
anvano
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Не могу понять, почему у меня странно работает функциональный индекс:


Код: plsql
1.
2.
3.
CREATE TABLE tmp_table (id NUMBER, val VARCHAR2(100));
CREATE INDEX idx_tmp_table ON tmp_table( CASE WHEN MOD(id,1000)=0 THEN 1 ELSE NULL END );
INSERT INTO tmp_table SELECT level, 'id='||LEVEL FROM dual CONNECT BY level <= 100000; 



Если делать просто запросы к таблице с данным условием - всё работает:
Код: plsql
1.
SELECT * FROM tmp_table WHERE  CASE WHEN MOD(id,1000)=0 THEN 1 ELSE NULL END = 1;



Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
 Plan Hash Value  : 1294569558 

----------------------------------------------------------------------------------------
| Id  | Operation                     | Name          | Rows | Bytes | Cost | Time     |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |               | 1000 | 68000 |    1 | 00:00:01 |
|   1 |   TABLE ACCESS BY INDEX ROWID | TMP_TABLE     | 1000 | 68000 |    1 | 00:00:01 |
| * 2 |    INDEX RANGE SCAN           | IDX_TMP_TABLE | 1000 |       |    1 | 00:00:01 |
----------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
------------------------------------------
* 2 - access(CASE MOD("ID",100) WHEN 0 THEN 1 ELSE NULL END =1)

Если потом к данной таблице джойнить обычные таблицы - всё тоже очень хорошо - индекс подхватывается.

Стоит только добавить в запрос объектный тип - всё, как отрубает, даже хинтами не получается заставить использовать индекс.

Код: plsql
1.
2.
3.
4.
SELECT /*+ leading(t1) index(t1 idx_tmp_table)  */  t1.* 
FROM tmp_table t1 , TABLE(SYS.Odcinumberlist(1000,2000,3000)) t2
WHERE CASE WHEN MOD(t1.id,1000)=0 THEN 1 ELSE NULL END = 1
AND t2.column_value = t1.id


Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
 Plan Hash Value  : 2034260201 

------------------------------------------------------------------------------------------------
| Id  | Operation                                | Name      | Rows | Bytes  | Cost | Time     |
------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                         |           |    7 |    469 | 9398 | 00:00:04 |
| * 1 |   HASH JOIN                              |           |    7 |    469 | 9398 | 00:00:04 |
| * 2 |    TABLE ACCESS FULL                     | TMP_TABLE | 8229 | 534885 | 9366 | 00:00:04 |
|   3 |    COLLECTION ITERATOR CONSTRUCTOR FETCH |           | 8168 |  16336 |   32 | 00:00:01 |
------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
------------------------------------------
* 1 - access("T1"."ID"=VALUE(KOKBF$))
* 2 - filter(MOD("T1"."ID",1000)=0)

Сбор статистики не помогает - даже если сделать в таблице 10 миллионов записей - и индекс такой, что под функциональный индекс попадает только сотня (т.е селективность огромная), а всё равно не хочет выбирать по индексу.

Это что какое-то ограничение оракловое ?

Код: plaintext
1.
2.
3.
4.
5.
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
PL/SQL Release 11.2.0.4.0 - Production
"CORE	11.2.0.4.0	Production"
TNS for Linux: Version 11.2.0.4.0 - Production
NLSRTL Version 11.2.0.4.0 - Production


--------------------------------------------------------------
Запомните, товарищи офицеры, чтобы ничего не делать, надо уметь делать все.
...
Рейтинг: 0 / 0
Добавление объектного типа в запрос - отрубает использование функциональных индексов
    #39946082
Alexander Anokhin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Скорее всего фиксили какой-нибудь ORA-600, добавили ограничение. Поднимай SR, пусть ослабляют ограничение взад.
...
Рейтинг: 0 / 0
Добавление объектного типа в запрос - отрубает использование функциональных индексов
    #39946084
Фотография Sayan Malakshinov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Модератор форума
anvano,

да, ограничение известное. TABLE() вообще вызывает кучу проблем.
На современных версиях это нормально обходится добавлением виртуального столбца:
Код: plsql
1.
2.
3.
4.
5.
CREATE TABLE tmp_table2 (id NUMBER, val VARCHAR2(100));
INSERT INTO tmp_table2 SELECT level, 'id='||LEVEL FROM dual CONNECT BY level <= 100000; 
alter table tmp_table2 
  add gen_value as (CASE WHEN MOD(id,1000)=0 THEN 1 ELSE NULL END);
CREATE INDEX idx_tmp_table2 ON tmp_table2( gen_value );


Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
SQL> explain plan for
  2  SELECT  t1.*
  3  FROM tmp_table2 t1 , TABLE(SYS.Odcinumberlist(1000,2000,3000)) t2
  4  WHERE gen_value = 1
  5  AND t2.column_value = t1.id;
SQL> @xplan +outline

P_FORMAT
------------------------
typical +outline

PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------
Plan hash value: 384700933

---------------------------------------------------------------------------------------------------------
| Id  | Operation                              | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                       |                |     7 |   490 |    31   (0)| 00:00:01 |
|*  1 |  HASH JOIN                             |                |     7 |   490 |    31   (0)| 00:00:01 |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED  | TMP_TABLE2     |   100 |  6800 |     2   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN                    | IDX_TMP_TABLE2 |   100 |       |     1   (0)| 00:00:01 |
|   4 |   COLLECTION ITERATOR CONSTRUCTOR FETCH|                |  8168 | 16336 |    29   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------------

Outline Data
-------------

  /*+
      BEGIN_OUTLINE_DATA
      USE_HASH(@"SEL$F5BB74E1" "KOKBF$0"@"SEL$2")
      LEADING(@"SEL$F5BB74E1" "T1"@"SEL$1" "KOKBF$0"@"SEL$2")
      FULL(@"SEL$F5BB74E1" "KOKBF$0"@"SEL$2")
      BATCH_TABLE_ACCESS_BY_ROWID(@"SEL$F5BB74E1" "T1"@"SEL$1")
      INDEX_RS_ASC(@"SEL$F5BB74E1" "T1"@"SEL$1" "IDX_TMP_TABLE2")
      OUTLINE(@"SEL$2")
      OUTLINE(@"SEL$1")
      MERGE(@"SEL$2" >"SEL$1")
      OUTLINE_LEAF(@"SEL$F5BB74E1")
      ALL_ROWS
      DB_VERSION('18.1.0')
      OPTIMIZER_FEATURES_ENABLE('18.1.0')
      IGNORE_OPTIM_EMBEDDED_HINTS
      END_OUTLINE_DATA
  */

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("T1"."ID"=VALUE(KOKBF$))
   3 - access("GEN_VALUE"=1)


А для вашей старой версии есть воркэраунд, но он крайне некрасивый и его не стоит использовать: получить имя этого скрытого столбца созданного для FBI и использовать его:
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
SQL> select column_name from user_ind_columns where table_name='TMP_TABLE';

COLUMN_NAME
--------------------------------------------------------
SYS_NC00003$

SQL> explain plan for
  2  SELECT /*+ leading(t1) index(t1 (SYS_NC00003$)) cardinality(t2 3) */  t1.*
  3  FROM tmp_table t1 , TABLE(SYS.Odcinumberlist(1000,2000,3000)) t2
  4  WHERE t1.SYS_NC00003$ = 1
  5  AND t2.column_value = t1.id;

SQL> @xplan +outline

P_FORMAT
------------------------
typical +outline

PLAN_TABLE_OUTPUT
-------------------------------------------------------
Plan hash value: 1861203920

--------------------------------------------------------------------------------------------------------
| Id  | Operation                              | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                       |               |     1 |    70 |    30   (0)| 00:00:01 |
|*  1 |  HASH JOIN                             |               |     1 |    70 |    30   (0)| 00:00:01 |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED  | TMP_TABLE     |     1 |    68 |     1   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN                    | IDX_TMP_TABLE |     1 |       |     1   (0)| 00:00:01 |
|   4 |   COLLECTION ITERATOR CONSTRUCTOR FETCH|               |     3 |     6 |    29   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------------

Outline Data
-------------

  /*+
      BEGIN_OUTLINE_DATA
      USE_HASH(@"SEL$F5BB74E1" "KOKBF$0"@"SEL$2")
      LEADING(@"SEL$F5BB74E1" "T1"@"SEL$1" "KOKBF$0"@"SEL$2")
      FULL(@"SEL$F5BB74E1" "KOKBF$0"@"SEL$2")
      BATCH_TABLE_ACCESS_BY_ROWID(@"SEL$F5BB74E1" "T1"@"SEL$1")
      INDEX_RS_ASC(@"SEL$F5BB74E1" "T1"@"SEL$1" "IDX_TMP_TABLE")
      OUTLINE(@"SEL$2")
      OUTLINE(@"SEL$1")
      MERGE(@"SEL$2" >"SEL$1")
      OUTLINE_LEAF(@"SEL$F5BB74E1")
      ALL_ROWS
      DB_VERSION('18.1.0')
      OPTIMIZER_FEATURES_ENABLE('18.1.0')
      IGNORE_OPTIM_EMBEDDED_HINTS
      END_OUTLINE_DATA
  */

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("T1"."ID"=VALUE(KOKBF$))
   3 - access(CASE MOD("ID",1000) WHEN 0 THEN 1 ELSE NULL END =1)



Поэтому лучше просто создать еще одну инлайн вью, в которой и расположить все нужные предикаты:
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
SELECT /*+ leading(t1) */  t1.* 
FROM (select/*+ no_merge  */ * from tmp_table t1 WHERE CASE WHEN MOD(t1.id,1000)=0 THEN 1 ELSE NULL END = 1 ) t1
     ,TABLE(SYS.Odcinumberlist(1000,2000,3000)) t2
WHERE t2.column_value = t1.id;

Plan hash value: 1288246070

--------------------------------------------------------------------------------------------------------
| Id  | Operation                              | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                       |               |    82 |  5494 |    30   (0)| 00:00:01 |
|*  1 |  HASH JOIN                             |               |    82 |  5494 |    30   (0)| 00:00:01 |
|   2 |   VIEW                                 |               |     1 |    65 |     1   (0)| 00:00:01 |
|   3 |    TABLE ACCESS BY INDEX ROWID BATCHED | TMP_TABLE     |     1 |    68 |     1   (0)| 00:00:01 |
|*  4 |     INDEX RANGE SCAN                   | IDX_TMP_TABLE |     1 |       |     1   (0)| 00:00:01 |
|   5 |   COLLECTION ITERATOR CONSTRUCTOR FETCH|               |  8168 | 16336 |    29   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("T1"."ID"=VALUE(KOKBF$))
   4 - access(CASE MOD("ID",1000) WHEN 0 THEN 1 ELSE NULL END =1)
...
Рейтинг: 0 / 0
Добавление объектного типа в запрос - отрубает использование функциональных индексов
    #39946085
Фотография Кобанчег
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
anvano,

Типичный костыль когда при добавлении чего-то перестает работать некий метод доступа - изолируй логику в non-mergeable inline view.
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
SQL> select *
  2    from (select t1.*
  3            from tmp_table t1
  4           where case when mod(t1.id, 1000) = 0 then 1 else null end = 1) t1,
  5         table(sys.odcinumberlist(1000, 2000, 3000)) t2
  6   where t2.column_value = t1.id;

------------------------------------------------------------
| Id  | Operation                              | Name      |
------------------------------------------------------------
|   0 | SELECT STATEMENT                       |           |
|   1 |  HASH JOIN                             |           |
|   2 |   TABLE ACCESS FULL                    | TMP_TABLE |
|   3 |   COLLECTION ITERATOR CONSTRUCTOR FETCH|           |
------------------------------------------------------------

SQL> select *
  2    from (select /*+ no_merge */ t1.*
  3            from tmp_table t1
  4           where case when mod(t1.id, 1000) = 0 then 1 else null end = 1) t1,
  5         table(sys.odcinumberlist(1000, 2000, 3000)) t2
  6   where t2.column_value = t1.id;

----------------------------------------------------------------
| Id  | Operation                              | Name          |
----------------------------------------------------------------
|   0 | SELECT STATEMENT                       |               |
|   1 |  HASH JOIN                             |               |
|   2 |   VIEW                                 |               |
|   3 |    TABLE ACCESS BY INDEX ROWID BATCHED | TMP_TABLE     |
|   4 |     INDEX RANGE SCAN                   | IDX_TMP_TABLE |
|   5 |   COLLECTION ITERATOR CONSTRUCTOR FETCH|               |
----------------------------------------------------------------
...
Рейтинг: 0 / 0
Добавление объектного типа в запрос - отрубает использование функциональных индексов
    #39946273
anvano
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Да, спасибо, inline view помог
...
Рейтинг: 0 / 0
Добавление объектного типа в запрос - отрубает использование функциональных индексов
    #39946286
Фотография Sayan Malakshinov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Модератор форума
anvano,

забыл добавить еще один старый воркэраунд для оракла <12: создать обычную вью, где одним из полей и будет этот вычислимый столбец и использовать ее с тем же хинтом no_merge - тогда нужные предикаты протолкнутся, да и использование станет намного удобнее. Естественно, эту вью использовать нужно только там, где это действительно нужно, дабы не напороться на другие проблемы с оптимизатором.

В примере, я вместо создания отдельной юзервью просто использую with, т.к. это просто пример
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
explain plan for
with v as (select t.*, CASE WHEN MOD(t.id,1000)=0 THEN 1 ELSE NULL END as fbi from tmp_table t)
SELECT /*+ leading(v t2) no_merge(v) */
  v.*
FROM v , TABLE(SYS.Odcinumberlist(1000,2000,3000)) t2
WHERE v.fbi = 1
AND t2.column_value = v.id
/

Plan hash value: 1288246070

--------------------------------------------------------------------------------------------------------
| Id  | Operation                              | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                       |               |     8 |   560 |   130   (0)| 00:00:01 |
|*  1 |  HASH JOIN                             |               |     8 |   560 |   130   (0)| 00:00:01 |
|   2 |   VIEW                                 |               |   100 |  6800 |   101   (0)| 00:00:01 |
|   3 |    TABLE ACCESS BY INDEX ROWID BATCHED | TMP_TABLE     |   100 |  1500 |   101   (0)| 00:00:01 |
|*  4 |     INDEX RANGE SCAN                   | IDX_TMP_TABLE |   100 |       |     1   (0)| 00:00:01 |
|   5 |   COLLECTION ITERATOR CONSTRUCTOR FETCH|               |  8168 | 16336 |    29   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("V"."ID"=VALUE(KOKBF$))
   4 - access(CASE MOD("ID",1000) WHEN 0 THEN 1 ELSE NULL END =1)

...
Рейтинг: 0 / 0
6 сообщений из 6, страница 1 из 1
Форумы / Oracle [игнор отключен] [закрыт для гостей] / Добавление объектного типа в запрос - отрубает использование функциональных индексов
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]