Oracle не использует индексы - PullRequest
0 голосов
/ 01 ноября 2019

Я планирую использовать схему базы данных с зависимыми таблицами. Нетривиальным является то, что каждая таблица также содержит историю записей. И запрос по дате должен искать значения для идентификатора для данной даты, так что это запрос для поиска и объединения записей разных таблиц.

select * from test1 t1
join test2 t2 on t2.t1_id = t1.base_id
join test3 t3 on t3.t2_id = t2.base_id
where t1.ver = (select max(ver) from test1 t where t1.base_id = t.base_id and t.ver <= '01.Jan.2025')
and t2.ver = (select max(ver) from test2 t where t2.base_id = t.base_id and t.ver <= '01.Jan.2025')
and t3.ver = (select max(ver) from test3 t where t2.base_id = t.base_id and t.ver <= '01.Jan.2025')
order by t1.id, t2.id, t3.id;

Я пробовал разные индексы - поэтому я предоставляюздесь только стандартные индексы. Это план атм:

    -----------------------------------------------------------------------------------------------------------------
| Id  | Operation                                  | Name       | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                           |            |     2 |   840 |       |  1149K  (1)| 00:00:45 |
|   1 |  SORT ORDER BY                             |            |     2 |   840 |    32M|  1149K  (1)| 00:00:45 |
|*  2 |   FILTER                                   |            |       |       |       |            |          |
|*  3 |    HASH JOIN                               |            | 75256 |    30M|    22M|   793K  (1)| 00:00:31 |
|   4 |     NESTED LOOPS                           |            | 75256 |    21M|       |   788K  (1)| 00:00:31 |
|*  5 |      HASH JOIN                             |            |   156K|    40M|  6128K|  4471   (1)| 00:00:01 |
|*  6 |       HASH JOIN                            |            | 39199 |  5665K|       |   489   (1)| 00:00:01 |
|   7 |        VIEW                                | VW_SQ_1    | 39199 |  1033K|       |    76   (4)| 00:00:01 |
|   8 |         HASH GROUP BY                      |            | 39199 |   459K|       |    76   (4)| 00:00:01 |
|*  9 |          INDEX FAST FULL SCAN              | ID_T1_VERB | 66707 |   781K|       |    74   (2)| 00:00:01 |
|  10 |        TABLE ACCESS FULL                   | TEST1      | 80000 |  9453K|       |   413   (1)| 00:00:01 |
|  11 |       TABLE ACCESS FULL                    | TEST2      |   320K|    38M|       |  1610   (1)| 00:00:01 |
|* 12 |      VIEW PUSHED PREDICATE                 | VW_SQ_2    |     1 |    22 |       |     5   (0)| 00:00:01 |
|* 13 |       FILTER                               |            |       |       |       |            |          |
|  14 |        SORT AGGREGATE                      |            |     1 |    12 |       |            |          |
|* 15 |         TABLE ACCESS BY INDEX ROWID BATCHED| TEST2      |     2 |    24 |       |     5   (0)| 00:00:01 |
|* 16 |          INDEX RANGE SCAN                  | ID_T2_B    |     2 |       |       |     3   (0)| 00:00:01 |
|  17 |     TABLE ACCESS FULL                      | TEST3      |   320K|    38M|       |  1610   (1)| 00:00:01 |
|  18 |    SORT AGGREGATE                          |            |     1 |    12 |       |            |          |
|* 19 |     TABLE ACCESS BY INDEX ROWID BATCHED    | TEST3      |     2 |    24 |       |     5   (0)| 00:00:01 |
|* 20 |      INDEX RANGE SCAN                      | ID_T3_B    |     2 |       |       |     3   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------------------

Какие индексы будут работать? Или есть способ написать лучший запрос?

TABLE ACCESS FULL убивает его для больших наборов данных, более объединенных таблиц или добавления фильтров атрибутов ...

Для создания этих 3 таблиц вы можете использовать этот sql:

create table test1
segment creation immediate nologging as
with generator as (
        select rownum id from dual
        connect by level <= 10000
)
select
        rownum                                id,
        rownum                                base_id,
        to_timestamp(to_date('01.Jan.2010') + rownum)  ver,
        mod(rownum-1,10)                      val,
        lpad('x',100,'x')                     padding
from
        generator       v1
order by
        dbms_random.value
;

insert into test1 (id, base_id, ver, val, padding)
(select id + 10000, id, to_timestamp(ver - id), val, padding from test1);
insert into test1 (id, base_id, ver, val, padding)
(select id + 20000, id, to_timestamp(ver - id), val, padding from test1);
insert into test1 (id, base_id, ver, val, padding)
(select id + 40000, id, to_timestamp(ver - id), val, padding from test1);

create table test2
segment creation immediate nologging as
with generator as (
        select rownum id from dual
        connect by level <= 10000
)
select
        rownum                                id,
        rownum                                base_id,
        to_timestamp(to_date('01.Jan.2010') + rownum)  ver,
        floor(dbms_random.value(1, 10000))    t1_id, 
        mod(rownum-1,10)                      val,
        lpad('x',100,'x')                     padding
from
        generator       v1
order by
        dbms_random.value
;

insert into test2 (id, base_id, ver, t1_id, val, padding)
(select id + 10000, id, to_timestamp(ver - id), t1_id, val, padding from test2);
insert into test2 (id, base_id, ver, t1_id, val, padding)
(select id + 20000, id, to_timestamp(ver - id), t1_id, val, padding from test2);
insert into test2 (id, base_id, ver, t1_id, val, padding)
(select id + 40000, id, to_timestamp(ver - id), t1_id, val, padding from test2);
insert into test2 (id, base_id, ver, t1_id, val, padding)
(select id + 80000, id, to_timestamp(ver - id), t1_id, val, padding from test2);
insert into test2 (id, base_id, ver, t1_id, val, padding)
(select id + 160000, id, to_timestamp(ver - id), t1_id, val, padding from test2);


create table test3
segment creation immediate nologging as
with generator as (
        select rownum id from dual
        connect by level <= 10000
)
select
        rownum                                id,
        rownum                                base_id,
        to_timestamp(to_date('01.Jan.3010') + rownum)  ver,
        floor(dbms_random.value(1, 10000))    t2_id, 
        mod(rownum-1,10)                      val,
        lpad('x',100,'x')                     padding
from
        generator       v1
order by
        dbms_random.value
;

insert into test3 (id, base_id, ver, t2_id, val, padding)
(select id + 10000, id, to_timestamp(ver - id), t2_id, val, padding from test3);
insert into test3 (id, base_id, ver, t2_id, val, padding)
(select id + 20000, id, to_timestamp(ver - id), t2_id, val, padding from test3);
insert into test3 (id, base_id, ver, t2_id, val, padding)
(select id + 40000, id, to_timestamp(ver - id), t2_id, val, padding from test3);
insert into test3 (id, base_id, ver, t2_id, val, padding)
(select id + 80000, id, to_timestamp(ver - id), t2_id, val, padding from test3);
insert into test3 (id, base_id, ver, t2_id, val, padding)
(select id + 160000, id, to_timestamp(ver - id), t2_id, val, padding from test3);


alter table test1 add constraint pk_t1 primary key (id);
alter table test2 add constraint pk_t2 primary key (id);
alter table test3 add constraint pk_t3 primary key (id);
alter table test1 add constraint fk_t1 foreign key (base_id) references test1(id);
alter table test2 add constraint fk_t2 foreign key (base_id) references test2(id);
alter table test3 add constraint fk_t3 foreign key (base_id) references test3(id);
alter table test2 add constraint fk_t2t1 foreign key (t1_id) references test1(id);
alter table test3 add constraint fk_t3t2 foreign key (t2_id) references test2(id);

create index id_t1_b on test1 (base_id);
create index id_t2_b on test2 (base_id);
create index id_t3_b on test3 (base_id);
create index id_t1_ver on test1 (ver);
create index id_t2_ver on test2 (ver);
create index id_t3_ver on test3 (ver);
create index id_t2_t1 on test2 (t1_id);
create index id_t2_t1v on test2 (t1_id, ver);
create index id_t3_t1 on test3 (t2_id);
create index id_t3_t1v on test3 (t2_id, ver);

1 Ответ

0 голосов
/ 01 ноября 2019

Использование предложения group by похоже на хитрость:

select * from test1 t1
join test2 t2 on t2.t1_id = t1.base_id
join test3 t3 on t3.t2_id = t2.base_id
where (t1.base_id, t1.ver) in (select t.base_id, max(ver) from test1 t where t.ver <= '01.Jan.2025' group by t.base_id)
and   (t2.base_id, t2.ver) in (select t.base_id, max(ver) from test2 t where t.ver <= '01.Jan.2025' group by t.base_id)
and   (t3.base_id, t3.ver) in (select t.base_id, max(ver) from test3 t where t.ver <= '01.Jan.2025' group by t.base_id)
order by t1.id, t2.id, t3.id;

теперь индексы ID_T*_BVER наконец используются

-------------------------------------------------------------------------------------------------------
| Id  | Operation                                | Name       | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                         |            |     1 |   446 |   794   (1)| 00:00:01 |
|   1 |  SORT ORDER BY                           |            |     1 |   446 |   794   (1)| 00:00:01 |
|   2 |   NESTED LOOPS SEMI                      |            |     1 |   446 |   793   (1)| 00:00:01 |
|   3 |    NESTED LOOPS                          |            |     1 |   422 |   790   (1)| 00:00:01 |
|   4 |     NESTED LOOPS SEMI                    |            |     1 |   297 |   687   (1)| 00:00:01 |
|   5 |      NESTED LOOPS                        |            |    47 | 12831 |   546   (1)| 00:00:01 |
|*  6 |       HASH JOIN RIGHT SEMI               |            |     3 |   444 |   489   (1)| 00:00:01 |
|   7 |        VIEW                              | VW_NSO_1   | 39163 |  1032K|    76   (4)| 00:00:01 |
|   8 |         HASH GROUP BY                    |            | 39163 |   458K|    76   (4)| 00:00:01 |
|*  9 |          INDEX FAST FULL SCAN            | ID_T1_BVER | 66499 |   779K|    74   (2)| 00:00:01 |
|  10 |        TABLE ACCESS FULL                 | TEST1      | 80000 |  9453K|   413   (1)| 00:00:01 |
|  11 |       TABLE ACCESS BY INDEX ROWID BATCHED| TEST2      |    18 |  2250 |    53   (0)| 00:00:01 |
|* 12 |        INDEX RANGE SCAN                  | ID_T2_T1   |    51 |       |     2   (0)| 00:00:01 |
|* 13 |      VIEW PUSHED PREDICATE               | VW_NSO_2   |     1 |    24 |     3   (0)| 00:00:01 |
|  14 |       SORT GROUP BY                      |            |     1 |    12 |     3   (0)| 00:00:01 |
|* 15 |        INDEX RANGE SCAN                  | ID_T2_BVER |     2 |    24 |     3   (0)| 00:00:01 |
|  16 |     TABLE ACCESS BY INDEX ROWID BATCHED  | TEST3      |    33 |  4125 |   103   (0)| 00:00:01 |
|* 17 |      INDEX RANGE SCAN                    | ID_T3_T1   |   100 |       |     2   (0)| 00:00:01 |
|* 18 |    VIEW PUSHED PREDICATE                 | VW_NSO_3   |     1 |    24 |     3   (0)| 00:00:01 |
|  19 |     SORT GROUP BY                        |            |     1 |    12 |     3   (0)| 00:00:01 |
|* 20 |      INDEX RANGE SCAN                    | ID_T3_BVER |     1 |    12 |     3   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------------

И так как веселье начинается, если веселье закончилось: План во время выполнения отличается от объясненного плана.

Решение: использование индекса силы:

select
/*+
    INDEX(t1 ID_T1_B)
    INDEX(t2 ID_T2_T1)
    INDEX(t3 ID_T3_T2)
 */
distinct * from test1 t1
join test2 t2 on t2.t1_id = t1.base_id
join test3 t3 on t3.t2_id = t2.base_id
where (t1.base_id, t1.ver) in (select t.base_id, max(ver) from test1 t where t.ver <= '01.Jan.2025' group by t.base_id)
and   (t2.base_id, t2.ver) in (select t.base_id, max(ver) from test2 t where t.ver <= t1.ver group by t.base_id)
and   (t3.base_id, t3.ver) in (select t.base_id, max(ver) from test3 t where t.ver <= t1.ver group by t.base_id)

, и время выполнения изменилось с> 1 ч до <20 с. </p>

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...