SQL 动态分组或数据透视和小计
Posted
技术标签:
【中文标题】SQL 动态分组或数据透视和小计【英文标题】:SQL Dynamic Grouping or Pivot and subtotals 【发布时间】:2019-09-18 15:26:19 【问题描述】:我需要在 SQL 中创建一个类似于以下内容的动态分组表:
Customer Rating Asset Type Exposure
ABC 9- apples -10
bananas 20
subtotal 10
DEF 5 grapes 5
GHI 8+ apples 15
bananas 9
subtotal 25
我已经尝试过枢轴和左连接,但我得到了我需要的东西。
代码
SELECT
A.CUSTOMER
,A.FRR as Rating
,C.PRODUCT_TYPE
,SUM(D.UTILIZATION) as Exposure
from table1 A
LEFT JOIN table1 B ON A.CUSTOMER =
B.CUSTOMER and A.PRODUCT_TYPE = 'Apples'
LEFT JOIN table1 C ON A.CUSTOMER =
C.CUSTOMER and A.PRODUCT_TYPE = 'Bananas'
LEFT JOIN table1 D ON A.CUSTOMER =
D.CUSTOMERand A.PRODUCT_TYPE = 'Grapes'
GROUP BY A.CUSTOMER
,A.FRR
,C.PRODUCT_TYPE
;
您能提供的任何帮助/指导都会很棒!
【问题讨论】:
所有这些相关数据真的存储在一个表的不同行的相同列中吗?您将 table1 加入自身 4 次 嗨 Caius,感谢您的回答,不,但这是我根据我在 google/*** 上找到的其他研究尝试的最后一次尝试 【参考方案1】:Oracle GROUP BY 支持 ROLLUP 和 GROUPING SETS,可为您提供更广泛的聚合:
所以您的基本查询如下所示:
SELECT
...
GROUP BY
A.CUSTOMER,A.FRR,C.PRODUCT_TYPE
它会产生:
Customer Rating Asset Type Exposure
ABC 9- apples -10
ABC 9- bananas 20
DEF 5 grapes 5
DEF 5 apples 15
DEF 5 bananas 9
如果您将其更改为按分组集分组:
SELECT
...
GROUP BY GROUPING SETS (
(A.CUSTOMER,A.FRR,C.PRODUCT_TYPE),(A.CUSTOMER,A.FRR)
)
您将获得对水果进行小计的额外行,水果名称为 NULL 表示它是一个总数(实际上 GROUPING() 函数表示它是“null 因为它是一个总数”还是“null 因为基础数据确实为空”但是..):
Customer Rating Asset Type Exposure
ABC 9- apples -10
ABC 9- bananas 20
ABC 9- NULL 10 <-- total of fruit for ABC/9-
DEF 5 grapes 5
DEF 5 apples 15
DEF 5 bananas 9
DEF 5 NULL 29 <-- total of fruit for DEF/5
GROUP BY
ROLLUP(A.CUSTOMER,A.FRR,C.PRODUCT_TYPE)
会做类似的事情,除了它从右到左一直到根(你会得到像 ABC NULL NULL 45
、DEF NULL NULL 62
和 NULL NULL NULL 107
这样的总计行
脚注:我假设您的示例数据有错误 - 我看不出 5+15+9 如何加起来等于 25,或者 DEF 和 GHI 是否应该相同等。最后,我不会如果行上的数据与上面的行相同,则不要尝试隐藏行上的数据,因为这意味着保持数据集有意义的唯一因素就是顺序。在用户看到它之前在前端做是可以的,但是在后端做它可能会在使用之前传递到几个地方,这意味着这些地方中的任何一个都可能会失去顺序并破坏数据。有关更多讨论,请参阅我刚刚在此处提出的非常相似的答案:Get count of particular category and further bifurcated sub category using SQL query
如果您仍然不顾一切地想隐藏第 N 行 X 列中与第 N-1 行 X 列中的数据相同的数据,您可以使用 LAG 分析函数拉取上一行数据并做出决定关于是否与该行数据相同是否为空
你问过是否只有一行是否总计。在这种情况下,我们也许可以做一些事情,比如计算明细行的数量,并排除那些总计行且它汇总的明细行数为 1 的行
为此,将以下行添加到查询的末尾:
HAVING NOT (GROUPING(PRODUCT_TYPE) = 1 and COUNT(DISTINCT PRODUCT_TYPE) = 1)
有关此的更多信息,请参阅 db fiddle:https://dbfiddle.uk/?rdbms=oracle_18&fiddle=935992f52f067c84b2899e2acea688a0
基本上它是如何工作的:
GROUPING(PRODUCT_TYPE)
为详细信息行返回 0,为小计行返回 1
COUNT(DISTINCT PRODUCT_TYPE)
为详细信息行返回 1,为总计行返回 N,其中 N 是滚动到该总计中的不同资产的数量:
10 行“apple”生成的 COUNT() 为 10,但 COUNT(DISTINCT) 为 1。
5 行“apple”和 5 行“orange”生成 COUNT() 为 10 和 COUNT(DISTINCT) 为 2。
通过排除不同资产类型的计数为 1 且该行是总计行的行,我们抑制了实质上重复详细信息行的总计行
【讨论】:
凯乌斯,谢谢你的帮助!这很棒!最后一个问题,分组集效果很好,如果每个客户有超过 1 个订单项,有没有办法只小计?再次感谢您的帮助!约翰 是的,由于我匆忙添加了这个例子,我的数学受到了影响 没有办法只选择性地应用分组集,但您当然可以删除它之后计算的行。我会在演示中添加一些东西 凯乌斯,谢谢!这简单明了,可以解决问题。出色的编码,我感谢您的解释!【参考方案2】:您可以将lag()
解析函数与rollup(customer, frr, product_type)
一起使用:
with t1 as
(
select customer, frr, product_type, sum(utilization) as exposure,
lag(customer) over (order by customer,product_type) as lg_cst,
lag(frr) over (order by customer,product_type) as lg_frr
from table1
group by rollup(customer, frr, product_type)
), t2 as
(
select
case when nvl(lg_cst,'') = customer
then
null
else
customer
end as customer,
case when nvl(lg_frr,'') = frr
then
null
else
frr
end as frr2,
case when nvl(lg_frr,'') = frr and product_type is null and customer is not null then
'subtotal'
when customer is null then
'Grand Total'
else
product_type
end as product_type, exposure
from t1
)
select *
from t2
where product_type is not null;
Demo
【讨论】:
以上是关于SQL 动态分组或数据透视和小计的主要内容,如果未能解决你的问题,请参考以下文章