MySQL 实现 UNPIVOT

在数据处理过程中,我们有时需要将表格的列转换为行,这个操作被称为 UNPIVOT。在 MySQL 中,UNPIVOT 操作并没有直接的语法支持,但是我们可以使用其他方法来实现这个操作。

本文将介绍 UNPIVOT 的概念和使用场景,并给出使用 MySQL 实现 UNPIVOT 的几种方法。

1. 什么是 UNPIVOT

在关系数据库中,表格的行代表实体,列表示实体的属性。而 UNPIVOT 操作可以将表格的列转换为行,即将属性转换为实体。

举个例子,假设我们有一个表格 students,其中包含了每个学生的姓名和成绩:

Name Math English Science
Alice 90 85 92
Bob 80 92 88
Carol 95 88 90

如果我们想要将每个学生的科目成绩转换为每个学生的单科成绩,我们可以使用 UNPIVOT 操作。

UNPIVOT 后的结果如下:

Name Subject Score
Alice Math 90
Alice English 85
Alice Science 92
Bob Math 80
Bob English 92
Bob Science 88
Carol Math 95
Carol English 88
Carol Science 90

2. UNPIVOT 的使用场景

UNPIVOT 操作在数据处理中非常常见,特别是在数据分析和报表生成过程中。

常见的使用场景包括:

  • 将表格的列转换为行,用于统计分析。
  • 数据集合和整理,方便后续处理。

3. 使用 MySQL 实现 UNPIVOT

在 MySQL 中,我们可以使用多种方法来实现 UNPIVOT 操作。下面分别介绍这几种方法。

3.1 使用 UNION ALL

UNION ALL 操作可以将多个 SELECT 语句的结果合并为一个结果集。

我们可以使用多个 SELECT 语句,每个 SELECT 语句返回一列数据,并使用 UNION ALL 将它们合并为一个结果集。

SELECT Name, 'Math' AS Subject, Math AS Score FROM students
UNION ALL
SELECT Name, 'English' AS Subject, English AS Score FROM students
UNION ALL
SELECT Name, 'Science' AS Subject, Science AS Score FROM students;

这个方法使用了多个 SELECT 语句,每个 SELECT 语句返回一个学科的成绩,并使用 UNION ALL 将它们合并为一个结果集。

3.2 使用 CROSS JOIN 和 CASE 语句

CROSS JOIN 可以将两个表格的每一行都组合在一起,生成一个新的表格。

我们可以使用多个 CASE 语句,每个 CASE 语句返回一个学科的成绩,并使用 CROSS JOIN 将它们组合为一个结果集。

SELECT Name,
    CASE Subject
        WHEN 'Math' THEN Math
        WHEN 'English' THEN English
        WHEN 'Science' THEN Science
    END AS Score
FROM students
CROSS JOIN (
    SELECT 'Math' AS Subject
    UNION ALL
    SELECT 'English' AS Subject
    UNION ALL
    SELECT 'Science' AS Subject
) subjects;

这个方法使用了一个嵌套的 SELECT 语句和 CASE 语句,通过 CROSS JOIN 将学科和成绩组合在一起。

3.3 使用 LATERAL JOIN (MySQL 8.0+)

MySQL 8.0 引入了 LATERAL JOIN,它可以在查询语句中引用之前的子查询的结果。

我们可以使用 LATERAL JOIN,将每个学科的成绩作为子查询,并将其引用到主查询中。

SELECT Name, Subject, Score
FROM students
LATERAL VIEW (
    SELECT 'Math' AS Subject, Math AS Score
    UNION ALL
    SELECT 'English' AS Subject, English AS Score
    UNION ALL
    SELECT 'Science' AS Subject, Science AS Score
) subjects;

这个方法使用了 LATERAL