【问题标题】:SQL ROLLUP or Union?SQL ROLLUP 还是联合?
【发布时间】:2017-02-21 11:47:33
【问题描述】:

我正在尝试获取条目总数,但不幸的是我不相信汇总会是最佳选择:

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 给了我小计,因为它包括数据组列。我只需要待处理项目的总计,但我需要显示数据组。使用 sum(pending_items) 选择查询 UNION 会更好吗?

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

谢谢!

【问题讨论】:

标签: sql oracle rollup


【解决方案1】:

为了清晰和性能,我会使用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 仍然有一个更好的计划,使用单表扫描。

【讨论】:

  • 在比较计划时要注意的重要事情不是“成本”(仅应针对相同查询的不同执行计划进行比较,而不是两个解决相同问题的不同查询,都正确但使用不同的方法)。需要注意的是union all 需要访问基表两次。尽管 Gordon 的意见相反(在另一个答案中),但这几乎肯定会使 union all 查询比 rollup 查询慢(并且可能慢得多)。
  • 感谢您的解释。我确实同意你的成本,但请记住,你的结果集并不是我想要显示数据的方式......似乎汇总需要进一步分组,这没有什么意义......我只需要盛大的所有行的总计,不需要进一步分组(考虑到已经执行了初始分组)。这有意义吗?
  • @Rob_E:我不明白这一点。给定我的示例表,结果应该是什么?
  • 基本上您的示例表与我的有点不同...在原始示例中,没有按 Datagroup 分组...
  • 只是显示相同的比较,没有初始分组
【解决方案2】:

你可以使用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 之间存在性能差异。但是请注意,这可以保证将汇总行作为结果集中的最后一行。

【讨论】:

  • 我相信你需要另外几个括号
  • union all 可能需要读取基表两次 - 为什么与只读取一次(使用rollup 解决方案)之间没有性能差异?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-07-15
  • 1970-01-01
  • 1970-01-01
  • 2016-08-03
  • 1970-01-01
  • 1970-01-01
  • 2013-11-19
相关资源
最近更新 更多