#sql #oracle #оптимизация_запросов
Есть временная таблица CREATE GLOBAL TEMPORARY TABLE "TMP_CONTROL_POINT_DETECT" ( "POINT_ID" NUMBER NOT NULL ENABLE, CONSTRAINT "TMP_CONTROL_POINT_DETECT_PK" PRIMARY KEY ("POINT_ID") ENABLE ) ON COMMIT PRESERVE ROWS ; И есть persistent таблица CREATE TABLE "CONTROL_POINTS_" ( "ID" NUMBER, "STATUS" NUMBER(1,0), CONSTRAINT "PK_CONTROL_POINTS_" PRIMARY KEY ("ID") USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS TABLESPACE "USERS" ENABLE ); Во временной таблице 200 записей, в персистентной 29 000 Делаю запрос SELECT cp.status FROM TMP_CONTROL_POINT_DETECT det JOIN CONTROL_POINTS_ cp ON ( det.POINT_ID = cp.ID ) и ужасаюсь плану ----------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ----------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 200 | 3800 | 18 (6)| 00:00:01 | |* 1 | HASH JOIN | | 200 | 3800 | 18 (6)| 00:00:01 | | 2 | TABLE ACCESS FULL| TMP_CONTROL_POINT_DETECT | 200 | 2600 | 2 (0)| 00:00:01 | | 3 | TABLE ACCESS FULL| CONTROL_POINTS_ | 29303 | 171K| 16 (7)| 00:00:01 | ----------------------------------------------------------------------------------------------- PLAN_TABLE_OUTPUT --------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("DET"."POINT_ID"="CP"."ID") Note ----- - dynamic statistics used: dynamic sampling (level=2) - this is an adaptive plan Потом меняю в запросе выбираемое поле SELECT cp.id FROM TMP_CONTROL_POINT_DETECT det JOIN CONTROL_POINTS_ cp ON ( det.POINT_ID = cp.ID ) И получаю ожидаемый план ----------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ----------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 200 | 3600 | 2 (0)| 00:00:01 | | 1 | NESTED LOOPS | | 200 | 3600 | 2 (0)| 00:00:01 | | 2 | TABLE ACCESS FULL| TMP_CONTROL_POINT_DETECT | 200 | 2600 | 2 (0)| 00:00:01 | |* 3 | INDEX UNIQUE SCAN| PK_CONTROL_POINTS_ | 1 | 5 | 0 (0)| 00:00:01 | ----------------------------------------------------------------------------------------------- PLAN_TABLE_OUTPUT --------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 3 - access("DET"."POINT_ID"="CP"."ID") Note ----- - dynamic statistics used: dynamic sampling (level=2) Кто нибудь может объяснить, что происходит? Откуда берется FULL SCAN? Сам запрос выбирает честные 200 записей Манипуляции типа SELECT cp.STATUS FROM CONTROL_POINTS_ cp WHERE cp.ID IN (SELECT ID FROM TMP_CONTROL_POINT_DETECT det) приводят к еще более удручающим последствиям ------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 5860K| 33M| 2820 (4)| 00:00:01 | | 1 | MERGE JOIN CARTESIAN| | 5860K| 33M| 2820 (4)| 00:00:01 | | 2 | TABLE ACCESS FULL | TMP_CONTROL_POINT_DETECT | 200 | | 2 (0)| 00:00:01 | | 3 | BUFFER SORT | | 29303 | 171K| 2818 (4)| 00:00:01 | | 4 | TABLE ACCESS FULL | CONTROL_POINTS_ | 29303 | 171K| 14 (0)| 00:00:01 | ------------------------------------------------------------------------------------------------- Update Временная таблица ни при чем. С персистентной такой же структуры и такими же данными та же картина
Ответы
Ответ 1
Оба плана абсолютно предсказуемы. во втором случае вам нужно только значение из индекса, поэтому берется индекс. А в первом случае нужно получать значение из области данных. При этом данных у вас 29000 записей, при средней длине записи около 4х байт. На диске вся таблица занимает 171 Кбайт (что видно в плане). При размере блока на диске в 4к это 43 блока. Получать 43 блока за 200 отдельных обращений по указателям из индекса мягко говоря накладно (Гарантировано будет прочитана вся таблица, причем каждый блок придется разбирать 4 раза). Full scan да еще и с hash join более чем оправдан. Использование индекса эффективно, когда с его помощью нужно обратиться не более чем к 10% всех блоков таблицы.Ответ 2
Использование хинта index привел запрос в чувство SELECT /*+ index(cp PK_CONTROL_POINTS) */ cp.STATUS FROM TMP_CONTROL_POINT_DETECT det JOIN CONTROL_POINTS PARTITION (lic) cp ON ( det.POINT_ID = cp.ID ) -------------------------------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop | -------------------------------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 200 | 4000 | 202 (0)| 00:00:01 | | | | 1 | NESTED LOOPS | | 200 | 4000 | 202 (0)| 00:00:01 | | | | 2 | NESTED LOOPS | | 200 | 4000 | 202 (0)| 00:00:01 | | | | 3 | TABLE ACCESS FULL | TMP_CONTROL_POINT_DETECT | 200 | 2600 | 2 (0)| 00:00:01 | | | |* 4 | INDEX UNIQUE SCAN | PK_CONTROL_POINTS | 1 | | 0 (0)| 00:00:01 | | | | 5 | TABLE ACCESS BY GLOBAL INDEX ROWID| CONTROL_POINTS | 1 | 7 | 1 (0)| 00:00:01 | 2 | 2 | PLAN_TABLE_OUTPUT --------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 4 - access("DET"."POINT_ID"="CP"."ID") filter(TBL$OR$IDX$PART$NUM("CONTROL_POINTS",0,1,0,ROWID)=2)
Комментариев нет:
Отправить комментарий