Oracle где и индекс - PullRequest
       52

Oracle где и индекс

1 голос
/ 11 июля 2020

У меня такая проблема, вот структура данных у меня

create table tab (
     id_tab integer not null,
     val1 integer,
     val2 integer,
     val3 integer,
     val4 integer,
     val5 integer,
     val6 integer,
     val7 integer,
     val8 integer,
     val9 integer,
     CONSTRAINT tab_pk PRIMARY KEY (id_tab)
  );
  
  create index val1_index on tab (val1);
  create index val2_index on tab (val2);
  create index val3_index on tab (val3);
  create index val4_index on tab (val4);
  create index val5_index on tab (val5);
  create index val6_index on tab (val6);
  create index val7_index on tab (val7);
  create index val8_index on tab (val8);
  create index val9_index on tab (val9);

  create procedure test1 as
  begin
    for x in 1..10000
    loop
      insert into tab(id_tab, val1, val2, val3, val4, val5, val6, val7, val8, val9)
      values ((select nvl(max(id_tab), 0) + 1 from tab), 
              decode(round(dbms_random.value(0,2)), 1, null, dbms_random.value(1,9)), 
              decode(round(dbms_random.value(0,2)), 1, null, dbms_random.value(1,9)), 
              decode(round(dbms_random.value(0,2)), 1, null, dbms_random.value(1,9)), 
              decode(round(dbms_random.value(0,2)), 1, null, dbms_random.value(1,9)), 
              decode(round(dbms_random.value(0,2)), 1, null, dbms_random.value(1,9)), 
              decode(round(dbms_random.value(0,2)), 1, null, dbms_random.value(1,9)),
              decode(round(dbms_random.value(0,2)), 1, null, dbms_random.value(1,9)), 
              decode(round(dbms_random.value(0,2)), 1, null, dbms_random.value(1,9)), 
              decode(round(dbms_random.value(0,2)), 1, null, dbms_random.value(1,9)));
    end loop;
  end;
  /

BEGIN
test1;
-- for my example, to find value:
insert into tab (id_tab, val1, val2, val3, val4, val5, val6, val7, val8, val9) 
values(-1, 3, 1, null, 5, 2, 1, 9, null, 1);
END;
/

Сейчас ищу результат

SELECT * FROM tab
where 
(decode(val1, 3, 1) = 1) and -- it's like: (val1 = 3 or (val1 is null and 3 is null) 
(decode(val2, 1, 1) = 1) and 
(decode(val3, null, 1) = 1) and -- it's like: (val1 = null or (val1 is null and null is null)
(decode(val4, 5, 1) = 1) and
(decode(val5, 2, 1) = 1) and
(decode(val6, 1, 1) = 1) and
(decode(val7, 9, 1) = 1) and
(decode(val8, null, 1) = 1) and
(decode(val9, 1, 1) = 1)

А у меня ожидаемый результат:

enter image description here

The problem is that I have about a million to find such combinations and it takes about an hour, the question is whether it is possible to use other indexes or otherwise to construct a query (where) to make the search for such combinations time-efficient?

here's the example: https://dbfiddle.uk/?rdbms=oracle_18&fiddle=ed77f058517197d4468e143f9deab3e1

DB: Oracle 11 Standard Edition One

Ответы [ 3 ]

1 голос
/ 11 июля 2020

Если вы используете функцию в индексе после его создания, она будет подавлена ​​этой функцией и станет бесполезной (как эта (decode(val1, 3, 1) = 1)). С другой стороны, вам не следует использовать так много index. Это не лучшая практика, и вы должны это знать. Каждый созданный вами индекс имеет стоимость на вашем диске.

Если вы настаиваете на использовании индексов, вам следует подумать о создании функции, которая получает некоторые входные данные и декодирует их на ваших условиях, а затем создайте свои индексы с использованием этой функции.

Индексы на основе функций

0 голосов
/ 11 июля 2020

Я добавил дополнительный текстовый столбец под названием «HA SH» в таблицу «TAB».

Я подсчитал его так:

VAL1||'*'||VAL2||'*'||val3||'*'||val4||'*'||val5||'*'||val6||'*'||val7||'*'||val8||'*'||val9

Я проиндексировал текстовый столбец «HA SH», время работы сократилось с часа до нескольких секунд, потому что поиск идет по одному индексу ... Здесь где-то проблема?

0 голосов
/ 11 июля 2020

Ну, вам вообще не следовало создавать какие-либо из этих индексов, потому что ваше предложение where влияет на все столбцы. Индексы весьма эффективны, когда ваше предложение where ищет определенный c столбец или столбцы, но не тогда, когда все столбцы в вашей таблице являются частью предиката. В этом случае CBO (оптимизатор на основе затрат) всегда будет использовать полное сканирование таблицы.

Позвольте мне показать вам пример с вашей моделью данных

SQL> create table tab_example (
     id_tab integer not null,
     val1 integer,
     val2 integer,
     val3 integer,
     val4 integer,
     val5 integer,
     val6 integer,
     val7 integer,
     val8 integer,
     val9 integer,
     CONSTRAINT tab_example_pk PRIMARY KEY (id_tab)
  )
/

Table created.

SQL> create index val1_index on tab_example (val1);
  create index val2_index on tab_example (val2);
  create index val3_index on tab_example (val3);
  create index val4_index on tab_example (val4);
  create index val5_index on tab_example (val5);
  create index val6_index on tab_example (val6);
  create index val7_index on tab_example (val7);
  create index val8_index on tab_example (val8);
  create index val9_index on tab_example (val9);
  
  Index created.

SQL>
Index created.

SQL>
Index created.

SQL>
Index created.

SQL>
Index created.

SQL>
Index created.

SQL>
Index created.

SQL>
Index created.

SQL>

Index created.

Я создаю потомок с одним миллионом строк в области видимости

SQL> create or replace procedure test1 as
begin
    for x in 1..1000000
    loop
      insert into tab_example(id_tab, val1, val2, val3, val4, val5, val6, val7, val8, val9)
      values ((select nvl(max(id_tab), 0) + 1 from tab_example), 
              decode(round(dbms_random.value(0,2)), 1, null, dbms_random.value(1,9)), 
              decode(round(dbms_random.value(0,2)), 1, null, dbms_random.value(1,9)), 
              decode(round(dbms_random.value(0,2)), 1, null, dbms_random.value(1,9)), 
              decode(round(dbms_random.value(0,2)), 1, null, dbms_random.value(1,9)), 
              decode(round(dbms_random.value(0,2)), 1, null, dbms_random.value(1,9)), 
              decode(round(dbms_random.value(0,2)), 1, null, dbms_random.value(1,9)),
              decode(round(dbms_random.value(0,2)), 1, null, dbms_random.value(1,9)), 
              decode(round(dbms_random.value(0,2)), 1, null, dbms_random.value(1,9)), 
              decode(round(dbms_random.value(0,2)), 1, null, dbms_random.value(1,9)));
    end loop;
  end;
  /

Procedure created.

SQL> set timing on 
SQL>  begin
  2  test1;
  3  insert into tab_example (id_tab, val1, val2, val3, val4, val5, val6, val7, val8, val9)
  4  values(-1, 3, 1, null, 5, 2, 1, 9, null, 1);
  5* end;
/

PL/SQL procedure successfully completed.

Elapsed: 00:08:09.34

Как видите, выполнение процедуры занимает много времени, в основном потому, что в операции задействовано 9 индексов. Помните, что любая операция DML с таблицей должна делать то же самое для индексов. В вашем случае эти индексы не предлагают никакой помощи в отношении вашего запроса, потому что, как я уже сказал, ваш предикат просматривает каждый столбец в вашей таблице.

Давайте вычислим статистику и посмотрим план объяснения вашего query

SQL> exec dbms_stats.gather_table_stats( 'MYUSER' , 'TAB_EXAMPLE' );

PL/SQL procedure successfully completed.

Elapsed: 00:00:04.19

SQL> set autotrace traceonly explain
SQL> SELECT * FROM tab_example
where
  2    3  (decode(val1, 3, 1) = 1) and -- it's like: (val1 = 3 or (val1 is null and 3 is null)
  4  (decode(val2, 1, 1) = 1) and
(decode(val3, null, 1) = 1) and -- it's like: (val1 = null or (val1 is null and null is null)
  5    6  (decode(val4, 5, 1) = 1) and
(decode(val5, 2, 1) = 1) and
(decode(val6, 1, 1) = 1) and
(decode(val7, 9, 1) = 1) and
  7    8    9   10  (decode(val8, null, 1) = 1) and
 11  (decode(val9, 1, 1) = 1);
Elapsed: 00:00:00.00

Execution Plan
----------------------------------------------------------
Plan hash value: 2592949226

---------------------------------------------------------------------------------
| Id  | Operation         | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |             |     1 |    23 |   709   (3)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| TAB_EXAMPLE |     1 |    23 |   709   (3)| 00:00:01 |
---------------------------------------------------------------------------------

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

   1 - filter(DECODE("VAL1",3,1)=1 AND DECODE("VAL2",1,1)=1 AND
              DECODE("VAL4",5,1)=1 AND DECODE("VAL5",2,1)=1 AND DECODE("VAL6",1,1)=1
              AND DECODE("VAL7",9,1)=1 AND DECODE("VAL9",1,1)=1 AND
              DECODE(TO_CHAR("VAL3"),NULL,1)=1 AND DECODE(TO_CHAR("VAL8"),NULL,1)=1)

В моем случае запрос почти ничего не берет, и план выполнения является правильным с учетом информации о предикате. Я проверяю каждый столбец, поэтому ПОЛНЫЙ ДОСТУП к таблице в этом случае лучше.

SQL> SELECT * FROM tab_example
where
  2    3  (decode(val1, 3, 1) = 1) and -- it's like: (val1 = 3 or (val1 is null and 3 is null)
(decode(val2, 1, 1) = 1) and
(decode(val3, null, 1) = 1) and -- it's like: (val1 = null or (val1 is null and null is null)
(decode(val4, 5, 1) = 1) and
  4    5    6    7  (decode(val5, 2, 1) = 1) and
(decode(val6, 1, 1) = 1) and
(decode(val7, 9, 1) = 1) and
  8    9   10  (decode(val8, null, 1) = 1) and
(decode(val9, 1, 1) = 1) 11  ;

    ID_TAB       VAL1       VAL2       VAL3       VAL4       VAL5       VAL6       VAL7       VAL8       VAL9
---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
        -1          3          1                     5          2          1          9                     1

Elapsed: 00:00:00.21

Если я удалю индексы (кроме принудительного индекса, используемого первичным ключом), посмотрим, сколько времени Мне нужно сделать вставку

SQL> declare
begin
for i in ( select index_name from dba_indexes where table_name = 'TAB_EXAMPLE' and index_name like 'VAL%' )
loop
        execute immediate ' drop index '||i.index_name||' ' ;
end loop;
end;
/
  2    3    4    5    6    7    8
PL/SQL procedure successfully completed.

Elapsed: 00:00:00.32

SQL> truncate table TAB_EXAMPLE ;

Table truncated.

Elapsed: 00:00:00.17

SQL> begin
test1;
insert into tab_example (id_tab, val1, val2, val3, val4, val5, val6, val7, val8, val9)
values(-1, 3, 1, null, 5, 2, 1, 9, null, 1);
end;
/  2    3    4    5    6

PL/SQL procedure successfully completed.

Elapsed: 00:02:41.01

Резюме: Правильная индексация - хорошее упражнение при построении физической модели данных. Индексирование всех столбцов - не лучший вариант и плохое решение. Бывают ситуации, например, возможно, вам понадобится IOT (Index Organized Table), но я не вижу здесь случая и не использовать столько бесполезных индексов.

Надеюсь, это немного поможет!

...