Оптимизация GROUP BY, которая содержит правила группировки с условием ИЛИ - PullRequest
3 голосов
/ 22 апреля 2019

Существует некоторая таблица T1 (в базе данных Oracle) с некоторыми полями A, B, C, D, E, F:

Upd 0: Пусть типы указанных полей совпадают.

Предположим, нам нужно сгруппировать нашу таблицу по следующему правилу: A & B & (C | D)

Upd 1: Выражение A & B & (C | D) можно преобразовать в следующее выражение:

(A & B & C) | (A & B & D).

Таким образом, для решения этой задачи мне нужно объединить два группирующих запроса для групп A, B, C и A, B, D:

select A, B, C, count(*) 
from T1 
group by A, B, C

  union all

select A, B, D, count(*) 
from T1 
group by A, B, D

Если правило группировки будет более сложным: A & B & (C | D) & (E | F), то решение будет более громоздким, поскольку мне нужно объединить запросы группирования для следующих групп:

A & B & C & E, A & B & D & E, A & B & C & F, A & B & D & F.

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

Upd 2: Я использовал короткие формы выражений A & B & (C | D) и A & B & (C | D) & (E | F), чтобы подчеркнуть, что они имеют общую часть A & B. И я не хочу, чтобы это вычислялось много раз.

1 Ответ

4 голосов
/ 24 апреля 2019

Предложение GROUPING SETS может упростить код и повысить производительность нескольких комбинаций групп.

Более простой код

Для примера давайте начнем с простой таблицы:

create table t1(a number, b number, c number, d number);
insert into t1
select 0,0,0,0 from dual union all
select 1,0,0,0 from dual union all
select 0,1,0,0 from dual union all
select 1,1,0,0 from dual union all
select 0,0,1,0 from dual union all
select 1,0,1,0 from dual union all
select 0,1,1,0 from dual union all
select 1,1,1,0 from dual union all
select 0,0,0,1 from dual union all
select 1,0,0,1 from dual union all
select 0,1,0,1 from dual union all
select 1,1,0,1 from dual union all
select 0,0,1,1 from dual union all
select 1,0,1,1 from dual union all
select 0,1,1,1 from dual union all
select 1,1,1,1 from dual;

Приведенный ниже запрос представляет группировку по «A & (B | C)». (В отличие от вашего примера, я собираюсь включить несколько пустых столбцов, чтобы продемонстрировать, как работает группировка.)

select a, b, null c, count(*)
from t1
group by a, b
union all
select a, null b, c, count(*)
from t1
group by a, c;

A   B   C   COUNT(*)
-   -   -   --------
1   0              4
0   0              4
1   1              4
0   1              4
1      0           4
0      0           4
1      1           4
0      1           4

Переписывание с GROUPING SETS создает те же результаты, что и предыдущий запрос:

select a, b, c, count(*)
from t1
group by grouping sets((a, b), (a, c));

Лучшая производительность

Выполнение вышеуказанных запросов с использованием explain plan for ... и затем select * from table(dbms_xplan.display(format => 'basic')); возвращает следующие планы выполнения.

Для UNION ALL версии:

------------------------------------
| Id  | Operation           | Name |
------------------------------------
|   0 | SELECT STATEMENT    |      |
|   1 |  UNION-ALL          |      |
|   2 |   HASH GROUP BY     |      |
|   3 |    TABLE ACCESS FULL| T1   |
|   4 |   HASH GROUP BY     |      |
|   5 |    TABLE ACCESS FULL| T1   |
------------------------------------

Для GROUPING SETS версии:

-------------------------------------------------------------------------------
| Id  | Operation                                | Name                       |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                         |                            |
|   1 |  TEMP TABLE TRANSFORMATION               |                            |
|   2 |   LOAD AS SELECT (CURSOR DURATION MEMORY)| SYS_TEMP_0FD9D6787_464CF95 |
|   3 |    TABLE ACCESS FULL                     | T1                         |
|   4 |   LOAD AS SELECT (CURSOR DURATION MEMORY)| SYS_TEMP_0FD9D6788_464CF95 |
|   5 |    HASH GROUP BY                         |                            |
|   6 |     TABLE ACCESS FULL                    | SYS_TEMP_0FD9D6787_464CF95 |
|   7 |   LOAD AS SELECT (CURSOR DURATION MEMORY)| SYS_TEMP_0FD9D6788_464CF95 |
|   8 |    HASH GROUP BY                         |                            |
|   9 |     TABLE ACCESS FULL                    | SYS_TEMP_0FD9D6787_464CF95 |
|  10 |   VIEW                                   |                            |
|  11 |    TABLE ACCESS FULL                     | SYS_TEMP_0FD9D6788_464CF95 |
-------------------------------------------------------------------------------

План выполнения UNION ALL считывает из исходной таблицы один раз для каждой отдельной группировки. План выполнения GROUPING SETS читает только из исходной таблицы только один раз, сохраняет информацию во временной таблице, а затем читает из этой временной таблицы.

Если в запросе используется только небольшое подмножество строк или только небольшое подмножество столбцов, план GROUPING SETS может быть значительно быстрее, поскольку он должен прочитать полные данные только один раз.

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