Dynamic Pivot 语句中的条件聚合

Posted

技术标签:

【中文标题】Dynamic Pivot 语句中的条件聚合【英文标题】:Conditional Aggregation within Dynamic Pivot Statement 【发布时间】:2021-02-16 04:55:48 【问题描述】:

我有 2 张桌子说:

Orders:
id | Name | Amount | Date
1  | ABC  | 100    | 2020-10-01
2  | XYZ  | 200    | 2020-10-01
3  | MNO  | 250    | 2020-11-01

Order_details:
id | Item | Qty
1  | A    | 2
1  | B    | 1
1  | C    | 3
2  | X    | 1
3  | A    | 4

现在我想使用下订单的日期获取数据。

如果我想获取2020-10-01 的数据,输出应该是这样的:

id | Name | Amount | Date       | Item1 | Qty | Item2 | Qty ...
1  | ABC  | 100    | 2020-10-01 | A     | 2   | B     | 1
2  | XYZ  | 200    | 2020-10-01 | X     | 1

我尝试使用子查询来获取它,但我不确定如何打印该数据。

提前致谢!

【问题讨论】:

PIVOT 在 mysql 中没有实现。 订单名称是否总是三个字母的字符串? 为什么是子查询? @SlavaRozhnev 不,它将包含订购人的姓名。举个例子,我写了一个 3 个字母的字符串。 @Strawberry 我的方法是使用子查询。任何其他方法也适用于我。 【参考方案1】:

对于每个订单的固定最大商品数量,您可以使用窗口函数和条件聚合:

select o.*,
    max(case when od.rn = 1 then item end) item1,
    max(case when od.rn = 1 then qty  end) qty1,
    max(case when od.rn = 2 then item end) item2,
    max(case when od.rn = 2 then qty  end) qty2,
    max(case when od.rn = 3 then item end) item3,
    max(case when od.rn = 3 then qty  end) qty3
from orders o
inner join (
    selet od.*, row_number() over(partition by id order by item) rn
    from order_details od
) od on od.id = o.id
group by o.id

您可以使用更多条件表达式扩展select 子句,以处理每个订单超过 3 件商品。

请注意,窗口函数仅在 MySQL 8.0 中可用。

【讨论】:

这里的问题是项目的数量也可以增加到 10 个。我们对每个订单的商品数量没有限制。【参考方案2】:

您可以在动态枢轴语句中使用条件聚合,它甚至适用于数据库版本5.5

SET SESSION group_concat_max_len = 18446744073709551615;
SET @sql = NULL;
SET @date = '2020-10-01';

SELECT GROUP_CONCAT(
           DISTINCT
             CONCAT(
                    'MAX(CASE WHEN rn = ', rn,' THEN Item END ) AS Item', rn,
                    ', MAX(CASE WHEN rn = ', rn,' THEN Qty END ) AS Qty'
                    )
       )
  INTO @sql
  FROM ( 
        SELECT *, @rn := IF(@i = id, @rn + 1, 1) AS rn, @i := id
          FROM Order_details
          JOIN (SELECT @i := 0, @rn := 0) i
         ORDER BY id, Item
  ) od;

SET @sql = CONCAT('SELECT o.id, o.name, o.amount, o.date,',@sql,
                   ' FROM Orders o
                     JOIN (
                           SELECT *, @rn := IF(@i = id, @rn + 1, 1) AS rn, @i := id
                             FROM Order_details
                             JOIN (SELECT @i := 0, @rn := 0) i
                            ORDER BY id, Item    
                          ) od
                       ON od.id = o.id
                    WHERE o.date = "',@date,'"
                    GROUP BY o.id, o.name, o.amount, o.date
                    ORDER BY o.id');

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

其中参数值可能会在第二行 (SET @date = '2020-10-01';) 内更新。顺便说一句,函数GROUP_CONCAT() 有一个长度上限(对于参数group_concat_max_len,默认值为1024),可能会被更新(最大值为18446744073709551615 ) 对于当前会话的情况,表有多个不同的项目,因此有很多列。

Demo

【讨论】:

谢谢,这在一开始对我有用,但现在它显示了一些语法错误。我没有更改此查询中的任何内容。 SQL Error [1064] [42000]: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'FROM Orders o JOIN ( ' at line 1 嗨@MdAkramKazmi,不客气。您能在演示中展示您遇到错误的案例吗? 解决了这个问题,当 order_details 表中有超过 11 个特定 ID 的条目时,查询不起作用。已在演示中更新。 @MdAkramKazmi 那么您需要增加参数 group_concat_max_len 的值。请查看更新后的答案。

以上是关于Dynamic Pivot 语句中的条件聚合的主要内容,如果未能解决你的问题,请参考以下文章

C#中的Dynamic

SQL中PIVOT 行列转换

如何用Pivot实现行列转换

PIVOT、UNPIVOT 转换行与列

python中的自定义pivot_ui聚合器?

SQL Server中行列转换 Pivot UnPivot