MySQL 将行旋转到动态列

MySQL Pivoting Rows to Dynamic Columns

我们有一个 table,其中包括如下 entrance/exit 人的记录:

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

有了这些数据,我们想 select 它们如下:

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

最简单的解决方案不是在 SQL 中执行此操作,而是通过简单查询获取所有数据:

SELECT person, day, time FROM WeHaveATable ORDER BY day, person, time;

然后编写应用程序代码以根据需要在网格中显示它。

这在 SQL 中很棘手的原因是 SQL 要求您在准备查询之前拼出 select 列表中的所有列。那是在它有机会读取数据以了解次数最多的人会有多少列之前。

SQL 无法通过读取数据并根据读取数据时发现的内容将更多列附加到 select 列表来生成“动态列”。

所以在SQL中做一个枢轴的方法是你首先必须知道有多少列。

SELECT MAX(c) FROM (SELECT COUNT(*) FROM WeHaveATable GROUP BY person) AS t;

然后使用 window 函数形成一个查询,对每个 person/day 的行进行编号,并在 pivot-table 查询中使用它,每次一列,向上到您在上一个查询中获得的最大次数。

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 中跳过解决这个问题的原因。执行我在顶部显示的简单查询,然后编写应用程序代码以按照需要将结果处理到网格中。

有关列需求的数据驱动列表,请参阅http://mysql.rjweb.org/doc.php/pivot

它构建 SELECT 并可选择运行它。