MySQL 将行转换为动态列
Posted
技术标签:
【中文标题】MySQL 将行转换为动态列【英文标题】:MySQL Pivoting Rows to Dynamic Columns 【发布时间】:2021-06-20 06:00:10 【问题描述】:我们有一个表格,其中包含以下人员出入记录:
Person | Day | Time |
---|---|---|
1 | 02/21/2021 | 08:10 |
2 | 02/21/2021 | 08:11 |
1 | 02/21/2021 | 08:45 |
1 | 02/21/2021 | 09:18 |
1 | 02/21/2021 | 10:45 |
7 | 02/21/2021 | 10:53 |
2 | 02/21/2021 | 17:06 |
1 | 02/21/2021 | 17:23 |
7 | 02/21/2021 | 17:31 |
1 | 02/22/2021 | 08:13 |
5 | 02/22/2021 | 08:19 |
2 | 02/22/2021 | 08:20 |
2 | 02/22/2021 | 08:23 |
5 | 02/22/2021 | 09:47 |
5 | 02/22/2021 | 11:03 |
5 | 02/22/2021 | 18:06 |
5 | 02/22/2021 | 19:08 |
2 | 02/22/2021 | 19:01 |
5 | 02/22/2021 | 22:37 |
5 | 02/23/2021 | 08:15 |
1 | 02/23/2021 | 08:15 |
1 | 02/23/2021 | 14:30 |
5 | 02/23/2021 | 17:05 |
有了这些数据,我们想如下选择它们:
Person | Day | Time1 | Time2 | Time3 | Time4 | Time5 | Time6 |
---|---|---|---|---|---|---|---|
1 | 02/21/2021 | 08:10 | 08:45 | 09:18 | 10:45 | 17:23 | |
2 | 02/21/2021 | 08:11 | 17:06 | ||||
7 | 02/21/2021 | 10:53 | 17:31 | ||||
1 | 02/22/2021 | 08:13 | |||||
2 | 02/22/2021 | 08:20 | 08:23 | 19:01 | |||
5 | 02/22/2021 | 08:19 | 09:47 | 11:03 | 18:06 | 19:08 | 22:37 |
1 | 02/23/2021 | 08:15 | 14:30 | ||||
5 | 02/23/2021 | 08:15 | 17:05 |
【问题讨论】:
考虑将日期和时间存储为单个实体,并考虑处理应用程序代码中数据显示的问题 枚举一个人+日期的行,然后旋转。 你能写给我吗? 【参考方案1】:最简单的解决方案不是在 SQL 中执行此操作,而是通过一个简单的查询来获取所有数据:
SELECT person, day, time FROM WeHaveATable ORDER BY day, person, time;
然后编写应用程序代码以根据需要在网格中显示它。
这在 SQL 中很棘手的原因是 SQL 要求您在准备查询之前拼出选择列表中的所有列。那是在它有机会读取数据以了解次数最多的人将有多少列之前。
SQL 中无法通过读取数据并根据在读取数据时发现的内容将更多列附加到选择列表来生成“动态列”。
因此,在 SQL 中进行数据透视的方法是,您首先必须知道有多少列。
SELECT MAX(c) FROM (SELECT COUNT(*) FROM WeHaveATable GROUP BY person) AS t;
然后形成一个查询,使用窗口函数对每人/天的行数进行编号,并在数据透视表查询中使用该查询,每次一列,直到您在上一个查询中获得的最大次数.
WITH cte AS (
SELECT person, day, time, ROW_NUMBER() OVER (PARTITION BY day, person ORDER BY time) AS colno
FROM WeHaveATable;
)
SELECT day, person,
MAX(CASE colno WHEN 1 THEN time END) AS Time1,
MAX(CASE colno WHEN 2 THEN time END) AS Time2,
MAX(CASE colno WHEN 3 THEN time END) AS Time3,
MAX(CASE colno WHEN 4 THEN time END) AS Time4,
MAX(CASE colno WHEN 5 THEN time END) AS Time5,
MAX(CASE colno WHEN 6 THEN time END) AS Time6
FROM cte
GROUP BY day, person;
如果这看起来像是很多令人困惑的细致工作,那你是对的。这就是为什么建议跳过在 SQL 中解决这个问题的原因。执行我在顶部显示的简单查询,然后编写应用程序代码以根据需要将结果处理到网格中。
【讨论】:
假设我们最多有 10 列并且需要分页和排序,这种方法不是比应用程序中的方法更有效吗? 也许吧。不同情况下的性能差异很大,唯一可以确定的方法是在您的服务器上使用您的数据尝试两种方式。【参考方案2】:有关列需求的数据驱动列表,请参阅http://mysql.rjweb.org/doc.php/pivot
它构建SELECT
并可选择运行它。
【讨论】:
以上是关于MySQL 将行转换为动态列的主要内容,如果未能解决你的问题,请参考以下文章