SQL ROLLUP или объединение?

Я пытаюсь получить общее количество записей, но, к сожалению, я не уверен, что объединение будет лучшим выбором:

SELECT BUSINESS_STATUS_NAME,
  PENDING_ITEMS,
  DATAGROUP
FROM PAYMENTS
WHERE STATUS LIKE '%PROCESS%';

Это производит:

BUSINESS_STATUS_NAME     PENDING_ITEMS     DATAGROUP
PROCESSING DATA          34                PRODUCT
PROCESSING INS           40                SERVICE

Я хотел бы получить общий итог ниже, но ROLLUP дает мне промежуточные итоги, потому что он включает столбец группы данных. Мне просто нужна общая сумма для ожидающих элементов, но мне нужно отобразить группу данных. Было бы лучше использовать UNION с запросом выбора суммы (pending_items)?

BUSINESS_STATUS_NAME     PENDING_ITEMS     DATAGROUP
PROCESSING DATA          34                PRODUCT
PROCESSING INS           40                SERVICE
GRAND TOTAL **           74

Спасибо!


person Rob_E    schedule 21.02.2017    source источник
comment
Используйте ROLLUP для повышения производительности. Если вам нужен промежуточный итог с помощью предложения with group by. >sql-plsql.blogspot.in/2010/10/rollup.html   -  person Prasanna Kumar J    schedule 21.02.2017


Ответы (2)


Я бы использовал ROLLUP как для ясности, так и для производительности.

Скажем, у вас есть пример таблицы:

create table payments (business_status_name, pending_items, datagroup) as (
    select 'PROCESSING DATA', 10, 'PRODUCT' from dual union all
    select 'PROCESSING DATA',  5, 'PRODUCT' from dual union all
    select 'PROCESSING DATA',  2, 'SERVICE' from dual union all
    select 'PROCESSING INS',  10, 'SERVICE' from dual union all
    select 'PROCESSING INS',  10, 'SERVICE' from dual union all
    select 'PROCESSING INS',  10, 'PRODUCT' from dual
)

Это способ с ROLLUP (обратите внимание на круглые скобки, чтобы изменить логику группировки):

SELECT BUSINESS_STATUS_NAME,
       SUM(PENDING_ITEMS) as PENDING_ITEMS,
       DATAGROUP
FROM PAYMENTS
GROUP BY ROLLUP ((BUSINESS_STATUS_NAME, DATAGROUP))

результат:

BUSINESS_STATUS PENDING_ITEMS DATAGRO
--------------- ------------- -------
PROCESSING INS             10 PRODUCT
PROCESSING INS             20 SERVICE
PROCESSING DATA            15 PRODUCT
PROCESSING DATA             2 SERVICE
                           47

план:

---------------------------------------------------------------------------------
| Id  | Operation            | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |          |     6 |   186 |     4  (25)| 00:00:01 |
|   1 |  SORT GROUP BY ROLLUP|          |     6 |   186 |     4  (25)| 00:00:01 |
|   2 |   TABLE ACCESS FULL  | PAYMENTS |     6 |   186 |     3   (0)| 00:00:01 |
---------------------------------------------------------------------------------

Это с UNION ALL:

SELECT BUSINESS_STATUS_NAME,
       SUM(PENDING_ITEMS) as PENDING_ITEMS,
       DATAGROUP
FROM PAYMENTS
GROUP BY BUSINESS_STATUS_NAME, DATAGROUP
UNION ALL
SELECT NULL, SUM(PENDING_ITEMS), NULL
FROM PAYMENTS;

результат тот же, что и с ROLLUP:

BUSINESS_STATUS PENDING_ITEMS DATAGRO
--------------- ------------- -------
PROCESSING INS             20 SERVICE
PROCESSING INS             10 PRODUCT
PROCESSING DATA            15 PRODUCT
PROCESSING DATA             2 SERVICE
                           47

план не очень, с TWO FULL SCANS:

--------------------------------------------------------------------------------
| Id  | Operation           | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |          |     7 |   199 |     7  (58)| 00:00:01 |
|   1 |  UNION-ALL          |          |       |       |            |          |
|   2 |   HASH GROUP BY     |          |     6 |   186 |     4  (25)| 00:00:01 |
|   3 |    TABLE ACCESS FULL| PAYMENTS |     6 |   186 |     3   (0)| 00:00:01 |
|   4 |   SORT AGGREGATE    |          |     1 |    13 |            |          |
|   5 |    TABLE ACCESS FULL| PAYMENTS |     6 |    78 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------------

Это, конечно, только небольшой пример с несколькими записями, без индексов... так что в реальных таблицах все может быть по-другому, но я все же считаю, что ROLLUP должен работать лучше, чем UNION ALL.

В более простом случае, точно равном вашему, это будут планы двух подходов:

SELECT BUSINESS_STATUS_NAME,
       SUM(PENDING_ITEMS) as PENDING_ITEMS,
       DATAGROUP
FROM PAYMENTS
GROUP BY ROLLUP ((BUSINESS_STATUS_NAME, DATAGROUP))

---------------------------------------------------------------------------------
| Id  | Operation            | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |          |     2 |    62 |     4  (25)| 00:00:01 |
|   1 |  SORT GROUP BY ROLLUP|          |     2 |    62 |     4  (25)| 00:00:01 |
|   2 |   TABLE ACCESS FULL  | PAYMENTS |     2 |    62 |     3   (0)| 00:00:01 |
---------------------------------------------------------------------------------

SELECT BUSINESS_STATUS_NAME,
       PENDING_ITEMS,
       DATAGROUP
FROM PAYMENTS
UNION ALL 
SELECT NULL, 
       SUM(PENDING_ITEMS),
       NULL
FROM PAYMENTS    

--------------------------------------------------------------------------------
| Id  | Operation           | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |          |     3 |    75 |     6  (50)| 00:00:01 |
|   1 |  UNION-ALL          |          |       |       |            |          |
|   2 |   TABLE ACCESS FULL | PAYMENTS |     2 |    62 |     3   (0)| 00:00:01 |
|   3 |   SORT AGGREGATE    |          |     1 |    13 |            |          |
|   4 |    TABLE ACCESS FULL| PAYMENTS |     2 |    26 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------------

ROLLUP по-прежнему имеет лучший план с одним сканированием таблицы.

person Aleksej    schedule 21.02.2017
comment
Важно отметить, что при сравнении планов не стоимость (которую следует сравнивать только для разных планов выполнения для одного и того же запроса, а не для двух разных запросов, решающих одну и ту же задачу, оба верны). но с использованием разных подходов). Следует отметить тот факт, что union all требует доступа к базовой таблице дважды. Несмотря на противоположное мнение Гордона (в другом ответе), это почти наверняка сделает запрос union all медленнее (и, вероятно, намного медленнее), чем запрос rollup. - person mathguy; 21.02.2017
comment
Спасибо за объяснение. Я согласен с вами по поводу стоимости, но имейте в виду, что ваш результирующий набор не совсем так, как я хотел бы отображать данные... Кажется, что сведение требует дальнейшей группировки, что не имеет большого смысла... Мне нужен только большой всего по всем строкам, дальнейшая группировка не требуется (учитывая, что начальная группировка уже выполнена). Имеет ли это смысл? - person Rob_E; 21.02.2017
comment
@Rob_E: я не могу понять. Каким должен быть результат с учетом моего примера таблицы? - person Aleksej; 21.02.2017
comment
В основном ваша таблица образцов немного отличается от моей... В исходном примере нет группировки по группе данных... - person Rob_E; 24.02.2017
comment
Только что показал такое же сравнение без начальной группировки - person Aleksej; 24.02.2017

Вы можете использовать rollup, но вам нужен запрос агрегации:

SELECT BUSINESS_STATUS_NAME,
       SUM(PENDING_ITEMS) as PENDING_ITEMS,
       DATAGROUP
FROM PAYMENTS
WHERE STATUS LIKE '%PROCESS%'
GROUP BY ROLLUP (BUSINESS_STATUS_NAME, DATAGROUP);

Я сомневаюсь, что между этим и union all есть разница в производительности. Обратите внимание, однако, что это гарантирует, что сводная строка будет последней строкой в ​​результирующем наборе.

person Gordon Linoff    schedule 21.02.2017
comment
Я считаю, что вам нужна еще пара скобок - person Aleksej; 21.02.2017
comment
union all, вероятно, потребует двукратного чтения базовой таблицы — почему не будет разницы в производительности между этим и однократным чтением (с решением rollup)? - person mathguy; 21.02.2017