如何在 oracle 数据库中为具有复杂聚合的数据透视编写等效的 sql 查询?

Posted

技术标签:

【中文标题】如何在 oracle 数据库中为具有复杂聚合的数据透视编写等效的 sql 查询?【英文标题】:How can i write an equivalent sql query for pivot with complex aggregations in oracle database? 【发布时间】:2021-03-06 17:22:23 【问题描述】:

在 Spark 中,我们可以提供带有枢轴的复杂聚合。 例如

project.groupBy("mgr","job").pivot("job").agg(sum(project.col("salary")).alias("ss") * count("*").alias("c"))

关于以下数据,即项目

mgr deptno salary job
APAC 10 100 CLERK
APAC 20 200 MANAGER
APAC 20 300 CLERK
APAC 10 400 CLERK
JPAC 20 1000 CLERK
JPAC 10 2000 MANAGER
EMEA 20 10000 CLERK
EMEA 20 40000 MANAGER
EMEA 20 30000 CLERK

输出:

mgr job CLERK MANAGER
EMEA MANAGER null 40000
JPAC MANAGER null 2000
EMEA CLERK 80000 null
JPAC CLERK 1000 null
APAC CLERK 2400 null
APAC MANAGER null 200

我可以在oracle数据库中使用pivot编写类似的sql查询吗?

【问题讨论】:

工资汇总输出与输入表不匹配。 CLERK 的 EMEA 工资将是 40000(10000+30000)而不是 80000。 它是 Sum * Count right。所以 40000 * 2。 好的,知道了。现在更改我的查询 【参考方案1】:

这里有你需要的两个版本(希望两者都能帮助你):

查询#1

select * from (
select mgr,job,salary from project
)
pivot
(
    sum(salary)
    for job in ('CLERK','MANAGER')
)

输出#1

MGR     JOB      'CLERK'    'MANAGER'
JPAC    CLERK    1000    - 
JPAC    MANAGER   -          2000
APAC    CLERK    800         - 
APAC    MANAGER   -          200
EMEA    CLERK    40000   - 
EMEA    MANAGER   -          40000

查询#2:

select * from (
select mgr,job,salary from project
)
pivot
(
    sum(salary)
    for Job  in ('CLERK','MANAGER')
)

输出#2:

MGR    'CLERK'  'MANAGER'
EMEA    40000    40000
APAC    800      200
JPAC    1000     2000

*查询#3 (SUM()COUNT()):

select * from (
select mgr,job,job JobToPivot,sum(salary)*count(*) salary from project
group by mgr,job
)
pivot
(
    max(salary)
    for JobToPivot  in ('CLERK','MANAGER')
)

输出#3:

MGR     JOB     'CLERK' 'MANAGER'
JPAC    CLERK    1000    - 
JPAC    MANAGER  -       2000
APAC    CLERK    2400    - 
APAC    MANAGER  -       200
EMEA    CLERK    80000   - 
EMEA    MANAGER  -       40000

*查询#4 (sum()COUNT()):

select * from (
select mgr,job,sum(salary)*count(*) salary from project
group by mgr,job
)
pivot
(
    max(salary)
    for Job  in ('CLERK','MANAGER')
)

输出#4:

MGR    'CLERK'  'MANAGER'
EMEA    80000   40000
APAC    2400    200
JPAC    1000    2000

【讨论】:

在 spark 查询中提供的聚合是 sum(project.col("salary")).alias("ss") * count("*").alias("c")。所以我们在 sql 查询中需要 sum 和 count 两者。 请看一下。 我认为查询 4 ​​需要一些修改,例如可以将别名与 sum(salary) count() 一起使用,并且像 max 这样的一些函数可以在枢轴内作为聚合传递。所以 select * from ( select mgr,job,sum(salary)*count() as sum_count Salary from project group by mgr,job ) pivot ( max(sum_count) for Job in ('CLERK','MANAGER' ) ) 可能是正确的查询。 由于答案是对的,所以没有着急。我已根据您的建议更改了答案。【参考方案2】:

您的结果集对我来说没有意义。其中之一不是更有用吗?

select mgr, job, sum(salary)
from project
group by mgr, job;

这会为每个 mgrjob 创建一个单独的行,但只有一个薪水。

或者:

select mgr,
       sum(case when job = 'CLERK' then salary end) as clerk,
       sum(case when job = 'MANAGER' then salary end) as manager
from project
group by mgr;

每个mgr 有一行,两个薪水列中都填充了薪水。

这两个都是标准 SQL(与 pivot 不同),应该在任何数据库中运行。

【讨论】:

你是对的。我们可以在不使用 pivot 关键字的情况下使用(聚合+过滤器)实现数据透视。这将适用于任何数据库。如果我们可以在 sql 中提供带有枢轴的复杂聚合,我想做的就像 spark 一样。

以上是关于如何在 oracle 数据库中为具有复杂聚合的数据透视编写等效的 sql 查询?的主要内容,如果未能解决你的问题,请参考以下文章

在DDD中为聚合提供多个存储库是否可以?

如何对具有聚合函数的 Oracle 查询进行分组

创建 Oracle 函数 - 如何减少样板代码

转置和聚合Oracle列数据

如何在 Oracle 中为所有用户和权限生成 DDL

如何在Oracle数据库查询中为没有数据的字段赋值为NULL