Страницы

Поиск по вопросам

воскресенье, 29 марта 2020 г.

JOIN с временной таблицей

#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)

Комментариев нет:

Отправить комментарий