powered by simpleCommunicator - 2.0.53     © 2025 Programmizd 02
Форумы / Oracle [игнор отключен] [закрыт для гостей] / Неправильное использование индекса по двум колонкам
14 сообщений из 14, страница 1 из 1
Неправильное использование индекса по двум колонкам
    #39843089
Nitro_Junkie
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Проверяю работу индексов в Oracle. Для этого генерирую таблицу на сто миллионов записей, с четырьмя колонками A,B,C,D
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
CREATE TABLE mytable
 (	"A" NUMBER, 
        "B" NUMBER, 
	"C" NUMBER, 
	"D" NUMBER, 
	"CDATA" VARCHAR2(300 BYTE), 
	"CID" NUMBER
)

INSERT INTO mytable (cid, A, B, C, D, cdata)
WITH
numbers ( n ) AS (
   SELECT 1 AS n FROM dual
   UNION ALL
   SELECT n + 1 AS n
   FROM   numbers
   WHERE  n < 10000000
)
SELECT n, round(dbms_random.value() * 100) + 1, 
                round(dbms_random.value() * 100) + 1, 
                round(dbms_random.value() * 100) + 1, 
                round(dbms_random.value() * 100) + 1,
                ('ab' || CONCAT(cid,'') || ' ') FROM numbers;



Добавляю индекс по A и C и делаю запрос с фильтрацией по A и C:

Код: plsql
1.
2.
3.
CREATE INDEX AC ON mytable(A,C);

SELECT COUNT(*) FROM (SELECT cid FROM mytable WHERE A=1 AND C=3)



Выполняется стабильно за 35 мс. Вот план:

Код: 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.
59.
SQL_ID  b8418wtac3846, child number 1
-------------------------------------
SELECT COUNT (DISTINCT cdata) FROM mytable WHERE A=1 AND C=3
 
Plan hash value: 1042283438
 
---------------------------------------------------------------------------------------------------
| Id  | Operation                              | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                       |          |       |       |   972 (100)|          |
|   1 |  SORT AGGREGATE                        |          |     1 |   152 |            |          |
|   2 |   VIEW                                 | VW_DAG_0 |     1 |   152 |   972   (1)| 00:00:01 |
|   3 |    HASH GROUP BY                       |          |     1 |    26 |   972   (1)| 00:00:01 |
|   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| MYTABLE  |   980 | 25480 |   971   (0)| 00:00:01 |
|*  5 |      INDEX RANGE SCAN                  | AC       |   980 |       |     5   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------
 
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
 
   1 - SEL$C33C846D
   2 - SEL$5771D262 / VW_DAG_0@SEL$C33C846D
   3 - SEL$5771D262
   4 - SEL$5771D262 / MYTABLE@SEL$1
   5 - SEL$5771D262 / MYTABLE@SEL$1
 
Outline Data
-------------
 
  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('19.1.0')
      DB_VERSION('19.1.0')
      ALL_ROWS
      OUTLINE_LEAF(@"SEL$5771D262")
      TRANSFORM_DISTINCT_AGG(@"SEL$1")
      OUTLINE_LEAF(@"SEL$C33C846D")
      OUTLINE(@"SEL$1")
      NO_ACCESS(@"SEL$C33C846D" "VW_DAG_0"@"SEL$C33C846D")
      INDEX_RS_ASC(@"SEL$5771D262" "MYTABLE"@"SEL$1" ("MYTABLE"."A" "MYTABLE"."C"))
      BATCH_TABLE_ACCESS_BY_ROWID(@"SEL$5771D262" "MYTABLE"@"SEL$1")
      USE_HASH_AGGREGATION(@"SEL$5771D262")
      END_OUTLINE_DATA
  */
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   5 - access("A"=1 AND "C"=3)
 
Column Projection Information (identified by operation id):
-----------------------------------------------------------
 
   1 - (#keys=0) COUNT("ITEM_1")[22]
   2 - (rowset=193) "ITEM_1"[VARCHAR2,300]
   3 - (rowset=193) "CDATA"[VARCHAR2,300]
   4 - "CDATA"[VARCHAR2,300]
   5 - "MYTABLE".ROWID[ROWID,10]



Теперь добавляю индекс по A и D, выполняю тот же запрос и вдруг Oracle переклинивает и он начинает использовать индекс AD (вместо AC) и выполнятся стабильно за 250мс.

Код: 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.
59.
60.
61.
62.
63.
64.
65.
CREATE INDEX AD ON mytable(A,D);

SELECT COUNT(*) FROM (SELECT cid FROM mytable WHERE A=1 AND C=3)

SQL_ID  b8418wtac3846, child number 0
-------------------------------------
SELECT COUNT (DISTINCT cdata) FROM mytable WHERE A=1 AND C=3
 
Plan hash value: 399952910
 
---------------------------------------------------------------------------------------------------
| Id  | Operation                              | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                       |          |       |       |   102 (100)|          |
|   1 |  SORT AGGREGATE                        |          |     1 |   152 |            |          |
|   2 |   VIEW                                 | VW_DAG_0 |     1 |   152 |   102   (1)| 00:00:01 |
|   3 |    HASH GROUP BY                       |          |     1 |    26 |   102   (1)| 00:00:01 |
|*  4 |     TABLE ACCESS BY INDEX ROWID BATCHED| MYTABLE  |   980 | 25480 |   101   (0)| 00:00:01 |
|*  5 |      INDEX RANGE SCAN                  | AD       |    99 |       |     3   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------
 
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
 
   1 - SEL$C33C846D
   2 - SEL$5771D262 / VW_DAG_0@SEL$C33C846D
   3 - SEL$5771D262
   4 - SEL$5771D262 / MYTABLE@SEL$1
   5 - SEL$5771D262 / MYTABLE@SEL$1
 
Outline Data
-------------
 
  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('19.1.0')
      DB_VERSION('19.1.0')
      ALL_ROWS
      OUTLINE_LEAF(@"SEL$5771D262")
      TRANSFORM_DISTINCT_AGG(@"SEL$1")
      OUTLINE_LEAF(@"SEL$C33C846D")
      OUTLINE(@"SEL$1")
      NO_ACCESS(@"SEL$C33C846D" "VW_DAG_0"@"SEL$C33C846D")
      INDEX_RS_ASC(@"SEL$5771D262" "MYTABLE"@"SEL$1" ("MYTABLE"."A" "MYTABLE"."D"))
      BATCH_TABLE_ACCESS_BY_ROWID(@"SEL$5771D262" "MYTABLE"@"SEL$1")
      USE_HASH_AGGREGATION(@"SEL$5771D262")
      END_OUTLINE_DATA
  */
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   4 - filter("C"=3)
   5 - access("A"=1)
 
Column Projection Information (identified by operation id):
-----------------------------------------------------------
 
   1 - (#keys=0) COUNT("ITEM_1")[22]
   2 - (rowset=193) "ITEM_1"[VARCHAR2,300]
   3 - (rowset=193) "CDATA"[VARCHAR2,300]
   4 - "CDATA"[VARCHAR2,300]
   5 - "MYTABLE".ROWID[ROWID,10]
 



Удаляю, все становится нормально, добавляю опять использует AD. В чем может быть дело?

Версия oracle:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
...
Рейтинг: 0 / 0
Неправильное использование индекса по двум колонкам
    #39843092
Nitro_Junkie
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Nitro_Junkie,
Ошибся, не
Код: plsql
1.
SELECT COUNT(*) FROM (SELECT cid FROM mytable WHERE A=1 AND C=3)


А:
Код: plsql
1.
SELECT COUNT (DISTINCT cdata) FROM mytable WHERE A=1 AND C=3



Собственно в плане это видно.
...
Рейтинг: 0 / 0
Неправильное использование индекса по двум колонкам
    #39843096
Фотография andrey_anonymous
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Nitro_JunkieВ чем может быть дело?
Статистику пересоберите после добавления индекса.
...
Рейтинг: 0 / 0
Неправильное использование индекса по двум колонкам
    #39843313
Nitro_Junkie
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andrey_anonymous,

Спасибо вроде помогло, хотя конечно не очевидно, что построение индекса не обновляет его статистику.

Но раз уже подняли вопрос с индексами. Такой вопрос, есть таблица с 10млн записей, в которой одна колонка SD заполнена только для 10к значений. По этой колонке есть индекс :

Код: 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.
CREATE TABLE SHIPMENTDETAIL
(	"ID" NUMBER(*,0), 
	"SHIPMENT" NUMBER(*,0), 
	"PRODUCT" NUMBER(*,0), 
	"QUANTITY" NUMBER, 
	"DATA" VARCHAR2(2000 BYTE), 
	"SD" NUMBER
);

INSERT INTO SHIPMENTDETAIL (id, shipment, product, quantity,sd)
WITH
numbers ( n ) AS (
   SELECT 1 AS n FROM dual
   UNION ALL
   SELECT n + 1 AS n
   FROM   numbers
   WHERE  n < 10000000
)

SELECT n, round(dbms_random.value() * 100000) + 1, 
                round(dbms_random.value() * 50000) + 1, 
                round(dbms_random.value() * 10) + 1, 
                CASE WHEN REMINDER(n,1000)=0 THEN n+1 ELSE NULL END FROM numbers;

CREATE INDEX shipmentdetail_sd ON shipmentdetail(sd);



Делаю запрос:

Код: plsql
1.
SELECT COUNT(DISTINCT data) FROM ShipmentDetail s1 WHERE s1.sd IS NOT NULL


Oracle упорно отказывается использовать этот индекс.

Код: 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.
SQL_ID  g4yr1h9rvvfh0, child number 0
-------------------------------------
SELECT COUNT(DISTINCT data) FROM ShipmentDetail s1 WHERE s1.sd IS NOT 
NULL
 
Plan hash value: 1160886572
 
---------------------------------------------------------------------------------------
| Id  | Operation            | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |                |       |       |  8894 (100)|          |
|   1 |  SORT AGGREGATE      |                |     1 |  1002 |            |          |
|   2 |   VIEW               | VW_DAG_0       |     1 |  1002 |  8894   (1)| 00:00:01 |
|   3 |    HASH GROUP BY     |                |     1 |    20 |  8894   (1)| 00:00:01 |
|*  4 |     TABLE ACCESS FULL| SHIPMENTDETAIL | 10000 |   195K|  8893   (1)| 00:00:01 |
---------------------------------------------------------------------------------------
 
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
 
   1 - SEL$C33C846D
   2 - SEL$5771D262 / VW_DAG_0@SEL$C33C846D
   3 - SEL$5771D262
   4 - SEL$5771D262 / S1@SEL$1
 
Outline Data
-------------
 
  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('19.1.0')
      DB_VERSION('19.1.0')
      ALL_ROWS
      OUTLINE_LEAF(@"SEL$5771D262")
      TRANSFORM_DISTINCT_AGG(@"SEL$1")
      OUTLINE_LEAF(@"SEL$C33C846D")
      OUTLINE(@"SEL$1")
      NO_ACCESS(@"SEL$C33C846D" "VW_DAG_0"@"SEL$C33C846D")
      FULL(@"SEL$5771D262" "S1"@"SEL$1")
      USE_HASH_AGGREGATION(@"SEL$5771D262")
      END_OUTLINE_DATA
  */
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   4 - filter("S1"."SD" IS NOT NULL)
 
Column Projection Information (identified by operation id):
-----------------------------------------------------------
 
   1 - (#keys=0) COUNT("ITEM_1")[22]
   2 - (rowset=31) "ITEM_1"[VARCHAR2,2000]
   3 - (rowset=31) "DATA"[VARCHAR2,2000]
   4 - (rowset=31) "DATA"[VARCHAR2,2000]
 



Все что можно уже пересчитал. Может нужно с какими-то хитрыми опциями колонку / индекс создавать?
...
Рейтинг: 0 / 0
Неправильное использование индекса по двум колонкам
    #39843363
Фотография andrey_anonymous
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Nitro_JunkieВсе что можно уже пересчитал. Может нужно с какими-то хитрыми опциями колонку / индекс создавать?
У этого индекса фактор кластеризации 10000.
Фактически, для выполнения указанного запроса серверу придется посетить практически все блоки таблицы, но по индексу - так нафига козе баян?
...если бы Вы sd подсчитывали, то индекс был бы к месту.
...
Рейтинг: 0 / 0
Неправильное использование индекса по двум колонкам
    #39843380
Nitro_Junkie
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andrey_anonymousNitro_JunkieВсе что можно уже пересчитал. Может нужно с какими-то хитрыми опциями колонку / индекс создавать?
У этого индекса фактор кластеризации 10000.
Фактически, для выполнения указанного запроса серверу придется посетить практически все блоки таблицы, но по индексу - так нафига козе баян?
...если бы Вы sd подсчитывали, то индекс был бы к месту.

Просто фокус, что этот запрос выполняется в среднем 350мс.

А
SELECT /*+ INDEX (s1 shipmentdetail_sd) */ COUNT(DISTINCT data) FROM ShipmentDetail s1 WHERE s1.sd IS NOT NULL

В среднем 60мс.

А есть какая-то настройка заставить Oracle агрессивнее использовать индексы (а точнее рандомный доступ, типа random_page_cost в Postgres)
...
Рейтинг: 0 / 0
Неправильное использование индекса по двум колонкам
    #39843383
Фотография andrey_anonymous
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Nitro_JunkieА есть какая-то настройка заставить Oracle агрессивнее использовать индексы
optimizer_index_cost_adj

Но по факту для особо критичных запросов надо не "заставлять" с риском поломать соседние запросы, а проектировать индексы как часть схемы данных.
К примеру, рассмотреть составной индекс (sd, data).
...
Рейтинг: 0 / 0
Неправильное использование индекса по двум колонкам
    #39843385
Фотография Elic
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andrey_anonymousК примеру, рассмотреть составной индекс (sd, data)."Есть запрос - давай создадим индекс" - смахивает на медвежий совет.
...
Рейтинг: 0 / 0
Неправильное использование индекса по двум колонкам
    #39843389
Фотография andrey_anonymous
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Elicandrey_anonymousК примеру, рассмотреть составной индекс (sd, data)."Есть запрос - давай создадим индекс" - смахивает на медвежий совет.
А если не выдергивать из контекста?
...
Рейтинг: 0 / 0
Неправильное использование индекса по двум колонкам
    #39843411
Фотография Elic
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andrey_anonymousElicпропущено...
"Есть запрос - давай создадим индекс" - смахивает на медвежий совет.
А если не выдергивать из контекста?Всё именно в контексте. Где хватает подсказки.
А ты действуешь по-хайнлайновски: "Вижу запрос - готов индексировать".
andrey_anonymousФактически, для выполнения указанного запроса серверу придется посетить практически все блоки таблицы, но по индексуТы странно рассуждаешь. 0,1% - это очень удобно для индексного доступа пр любом CF.
Тем более, что пример явно неправдоподобно неудачный: data не заполнен при потенциальной длине 2000. А при заполненности вред от от твоего индекса покроет пользу как бык овцу.
...
Рейтинг: 0 / 0
Неправильное использование индекса по двум колонкам
    #39843448
Фотография andrey_anonymous
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Elicandrey_anonymousА если не выдергивать из контекста?Всё именно в контексте.

И все-таки сделай усилие, прочитай целиком пост, из которого ты выдернул тему про составной индекс - там всего две строчки.

ElicА ты действуешь

Я же не вменяю тебе какой-либо образ действий, верно?
Буду признателен за взаимность.

ElicТы странно рассуждаешь. 0,1% - это очень удобно для индексного доступа пр любом CF.
Не при любом CF и не с любым предикатом.
У ТС короткие строки, причем проиндексированные строки так распределены, что для их отбора с указанным ТС предикатом придется посетить б о льшую часть блоков.

Я отсортировал исходную таблицу ТС относительно sd, фактор кластеризации индекса упал до 3 и индекс начал использоваться.
...
Рейтинг: 0 / 0
Неправильное использование индекса по двум колонкам
    #39843509
Nitro_Junkie
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andrey_anonymous,

Я уменьшил количество не null sd до 100 тоже начал использоваться. Меня на самом деле интересовала принципиальная возможность использования этого индекса (правда в другом запросе), так что я получил ответ на свой вопрос.

Но раз пошла такая пьянка, есть еще интересная вещь.

Кроме таблицы shipmentDetail сверху есть еще таблица product (с товарами).

Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
  CREATE TABLE PRODUCT
   (	"ID" NUMBER(*,0) 
	"NAME" VARCHAR2(100 BYTE)
   )

INSERT INTO Product (id, name)
WITH
numbers ( n ) AS (
   SELECT 1 AS n FROM dual
   UNION ALL
   SELECT n + 1 AS n
   FROM   numbers
   WHERE  n < 50000
)
SELECT n, 'Product '||n FROM numbers;



И индекс по product, shipment в shipmentDetail:

Код: plsql
1.
CREATE INDEX shipment_p_s ON shipmentDetail(product, shipment)



Делаю вот такой запрос:

Код: plsql
1.
SELECT MAX(shipment) AS ls FROM ShipmentDetail s WHERE s.product = 123



Получаю план
Код: 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.
SQL_ID  5fw3ztm7dz5hm, child number 0
-------------------------------------
SELECT MAX(shipment) AS ls FROM ShipmentDetail s WHERE s.product = 123
 
Plan hash value: 157083448
 
---------------------------------------------------------------------------------------------
| Id  | Operation                    | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |              |       |       |     3 (100)|          |
|   1 |  SORT AGGREGATE              |              |     1 |    10 |            |          |
|   2 |   FIRST ROW(!)               |              |     1 |    10 |     3   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN (MIN/MAX)| SHIPMENT_P_S |     1 |    10 |     3   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------
 
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
 
   1 - SEL$1
   3 - SEL$1 / S@SEL$1
 
Outline Data
-------------
 
  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('19.1.0')
      DB_VERSION('19.1.0')
      ALL_ROWS
      OUTLINE_LEAF(@"SEL$1")
      INDEX(@"SEL$1" "S"@"SEL$1" ("SHIPMENTDETAIL"."PRODUCT" "SHIPMENTDETAIL"."SHIPMENT"))
      END_OUTLINE_DATA
  */
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   3 - access("S"."PRODUCT"=123)
 
Column Projection Information (identified by operation id):
-----------------------------------------------------------
 
   1 - (#keys=0) MAX("SHIPMENT")[22]
   2 - "SHIPMENT"[NUMBER,22]
   3 - "SHIPMENT"[NUMBER,22]
 



Все хорошо. Oracle использует индекс и что самое главное догадывается вставить FIRST ROW.

Теперь делаю тот же запрос с Predicate push down

Код: plsql
1.
2.
3.
SELECT cc.ls FROM Product pr, (SELECT MAX(shipment) AS ls, s.product
FROM    ShipmentDetail s
        GROUP BY s.product) cc WHERE cc.product=pr.id AND pr.name LIKE 'Product 86%';



Код: 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.
59.
60.
61.
62.
SQL_ID  bm3dxvsk1p64p, child number 0
-------------------------------------
SELECT cc.ls FROM Product pr, (SELECT MAX(shipment) AS ls, s.product 
FROM    ShipmentDetail s         GROUP BY s.product) cc WHERE 
cc.product=pr.id AND pr.name LIKE 'Product 86%'
 
Plan hash value: 3614704251
 
------------------------------------------------------------------------------------
| Id  | Operation           | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |              |       |       |   120 (100)|          |
|   1 |  HASH GROUP BY      |              |  4898 |   138K|   120   (2)| 00:00:01 |
|   2 |   NESTED LOOPS      |              |  4898 |   138K|   118   (0)| 00:00:01 |
|*  3 |    TABLE ACCESS FULL| PRODUCT      |    25 |   475 |    68   (0)| 00:00:01 |
|*  4 |    INDEX RANGE SCAN | SHIPMENT_P_S |   198 |  1980 |     2   (0)| 00:00:01 |
------------------------------------------------------------------------------------
 
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
 
   1 - SEL$F5BB74E1
   3 - SEL$F5BB74E1 / PR@SEL$1
   4 - SEL$F5BB74E1 / S@SEL$2
 
Outline Data
-------------
 
  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('19.1.0')
      DB_VERSION('19.1.0')
      ALL_ROWS
      OUTLINE_LEAF(@"SEL$F5BB74E1")
      MERGE(@"SEL$2" >"SEL$1")
      OUTLINE(@"SEL$1")
      OUTLINE(@"SEL$2")
      FULL(@"SEL$F5BB74E1" "PR"@"SEL$1")
      INDEX(@"SEL$F5BB74E1" "S"@"SEL$2" ("SHIPMENTDETAIL"."PRODUCT" 
              "SHIPMENTDETAIL"."SHIPMENT"))
      LEADING(@"SEL$F5BB74E1" "PR"@"SEL$1" "S"@"SEL$2")
      USE_NL(@"SEL$F5BB74E1" "S"@"SEL$2")
      USE_HASH_AGGREGATION(@"SEL$F5BB74E1")
      END_OUTLINE_DATA
  */
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   3 - filter("PR"."NAME" LIKE 'Product 86%')
   4 - access("S"."PRODUCT"="PR"."ID")
 
Column Projection Information (identified by operation id):
-----------------------------------------------------------
 
   1 - (rowset=256) "S"."PRODUCT"[NUMBER,22], ROWID[ROWID,10], 
       MAX("SHIPMENT")[22]
   2 - ROWID[ROWID,10], "SHIPMENT"[NUMBER,22], "S"."PRODUCT"[NUMBER,22]
   3 - ROWID[ROWID,10], "PR"."ID"[NUMBER,22]
   4 - "SHIPMENT"[NUMBER,22], "S"."PRODUCT"[NUMBER,22]
 



В плане никакого FIRST ROW (должен быть над INDEX) нет. Ну и хер бы с ним, записей не много. Делаем с большим количеством записей (просто убирая фильтр по имени).

Код: plsql
1.
2.
3.
SELECT cc.ls FROM Product pr, (SELECT MAX(shipment) AS ls, s.product
FROM    ShipmentDetail s
        GROUP BY s.product) cc WHERE cc.product=pr.id;



Код: 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.
59.
60.
61.
62.
SQL_ID  98719kzpx2bbd, child number 0
-------------------------------------
SELECT cc.ls FROM Product pr, (SELECT MAX(shipment) AS ls, s.product 
FROM    ShipmentDetail s         GROUP BY s.product) cc WHERE 
cc.product=pr.id
 
Plan hash value: 1879424675
 
------------------------------------------------------------------------------------------------
| Id  | Operation               | Name         | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |              |       |       |       | 22329 (100)|          |
|*  1 |  HASH JOIN              |              | 50000 |  1513K|       | 22329   (2)| 00:00:01 |
|   2 |   TABLE ACCESS FULL     | PRODUCT      | 50000 |   244K|       |    68   (0)| 00:00:01 |
|   3 |   VIEW                  |              | 50536 |  1283K|       | 22260   (2)| 00:00:01 |
|   4 |    HASH GROUP BY        |              | 50536 |   493K|   191M| 22260   (2)| 00:00:01 |
|   5 |     INDEX FAST FULL SCAN| SHIPMENT_P_S |    10M|    95M|       |  7912   (1)| 00:00:01 |
------------------------------------------------------------------------------------------------
 
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
 
   1 - SEL$1
   2 - SEL$1 / PR@SEL$1
   3 - SEL$2 / CC@SEL$1
   4 - SEL$2
   5 - SEL$2 / S@SEL$2
 
Outline Data
-------------
 
  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('19.1.0')
      DB_VERSION('19.1.0')
      ALL_ROWS
      OUTLINE_LEAF(@"SEL$2")
      OUTLINE_LEAF(@"SEL$1")
      FULL(@"SEL$1" "PR"@"SEL$1")
      NO_ACCESS(@"SEL$1" "CC"@"SEL$1")
      LEADING(@"SEL$1" "PR"@"SEL$1" "CC"@"SEL$1")
      USE_HASH(@"SEL$1" "CC"@"SEL$1")
      INDEX_FFS(@"SEL$2" "S"@"SEL$2" ("SHIPMENTDETAIL"."PRODUCT" "SHIPMENTDETAIL"."SHIPMENT"))
      USE_HASH_AGGREGATION(@"SEL$2")
      END_OUTLINE_DATA
  */
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - access("CC"."PRODUCT"="PR"."ID")
 
Column Projection Information (identified by operation id):
-----------------------------------------------------------
 
   1 - (#keys=1; rowset=256) "CC"."LS"[NUMBER,22]
   2 - (rowset=256) "PR"."ID"[NUMBER,22]
   3 - (rowset=256) "CC"."LS"[NUMBER,22], "CC"."PRODUCT"[NUMBER,22]
   4 - (rowset=256) "S"."PRODUCT"[NUMBER,22], MAX("SHIPMENT")[22]
   5 - "SHIPMENT"[NUMBER,22], "S"."PRODUCT"[NUMBER,22]
 



Опять никакого FIRST ROW и запрос выполняется почти 3 секунды.

Переписываем запрос с явным rownum = 1 (SUM пришлось добавить, не в курсе как SQL Developer заставить не fetch'ить запросы)

Код: plsql
1.
SELECT SUM(l) FROM (SELECT (SELECT shipment FROM (SELECT shipment FROM ShipmentDetail s WHERE s.product = pr.id ORDER BY s.shipment) s WHERE rownum = 1) AS l FROM product pr)



Получаю нормальный план:
Код: 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.
59.
60.
61.
62.
63.
64.
65.
SQL_ID  8ut826vdv0gr6, child number 0
-------------------------------------
SELECT SUM(l) FROM (SELECT (SELECT shipment FROM (SELECT shipment FROM 
ShipmentDetail s WHERE s.product = pr.id ORDER BY s.shipment) s WHERE 
rownum = 1) AS l FROM product pr)
 
Plan hash value: 3251716916
 
-----------------------------------------------------------------------------------
| Id  | Operation          | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |              |       |       |   139K(100)|          |
|*  1 |  COUNT STOPKEY     |              |       |       |            |          |
|   2 |   VIEW             |              |     2 |    26 |     3   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN| SHIPMENT_P_S |     2 |    20 |     3   (0)| 00:00:01 |
|   4 |  SORT AGGREGATE    |              |     1 |     5 |            |          |
|   5 |   TABLE ACCESS FULL| PRODUCT      | 50000 |   244K|    68   (0)| 00:00:01 |
-----------------------------------------------------------------------------------
 
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
 
   1 - SEL$3       
   2 - SEL$4        / S@SEL$3
   3 - SEL$4        / S@SEL$4
   4 - SEL$F5BB74E1
   5 - SEL$F5BB74E1 / PR@SEL$2
 
Outline Data
-------------
 
  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('19.1.0')
      DB_VERSION('19.1.0')
      ALL_ROWS
      OUTLINE_LEAF(@"SEL$4")
      OUTLINE_LEAF(@"SEL$3")
      OUTLINE_LEAF(@"SEL$F5BB74E1")
      MERGE(@"SEL$2" >"SEL$1")
      OUTLINE(@"SEL$1")
      OUTLINE(@"SEL$2")
      FULL(@"SEL$F5BB74E1" "PR"@"SEL$2")
      NO_ACCESS(@"SEL$3" "S"@"SEL$3")
      INDEX(@"SEL$4" "S"@"SEL$4" ("SHIPMENTDETAIL"."PRODUCT" 
              "SHIPMENTDETAIL"."SHIPMENT"))
      END_OUTLINE_DATA
  */
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - filter(ROWNUM=1)
   3 - access("S"."PRODUCT"=:B1)
 
Column Projection Information (identified by operation id):
-----------------------------------------------------------
 
   1 - "SHIPMENT"[NUMBER,22]
   2 - "SHIPMENT"[NUMBER,22]
   3 - "S"."SHIPMENT"[NUMBER,22]
   4 - (#keys=0) SUM()[22]
   5 - "PR"."ID"[NUMBER,22]
 



То есть с FIRST ROW (в данном случае COUNT STOPKEY, но суть та же) и временем выполнения 400 мс в 10 раз меньше. Причем те же яйца в MS SQL.

Это что за фигня? Почему когда предикат равен значению оптимизатор понимает что надо взять одну запись, а при join predicate push down нет. Там же по идее одинаковые оптимизаторы должны быть. И здесь тяжело объяснить это cost'ами или неправильной статистикой, потому как FIRST ROW нужно по идее в любом случае вставлять.
...
Рейтинг: 0 / 0
Неправильное использование индекса по двум колонкам
    #39843517
Nitro_Junkie
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Nitro_Junkie,

Собственно в предпоследнем случае никакого даже predicate push down'а нет. Что в принципе понятно, так как без FIRST ROW у него очевидно cost выше будет. Но с FIRST ROW план должен был быть приблизительно как в последнем запросе (и он явно был бы дешевле).
...
Рейтинг: 0 / 0
Неправильное использование индекса по двум колонкам
    #39843607
Фотография Elic
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andrey_anonymousУ ТС короткие строкиЕщё раз, в это явное заблуждение ввел сам ТС. Ибо на его тестовых данных запрос не имеет практического смысла.
...
Рейтинг: 0 / 0
14 сообщений из 14, страница 1 из 1
Форумы / Oracle [игнор отключен] [закрыт для гостей] / Неправильное использование индекса по двум колонкам
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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