在 MySQL 中,行转列(Row to Column) 和 列转行(Column to Row) 是常见的操作,用于将数据以不同的形式进行展示。通常,行转列用于将多个行的数据合并成一行,而列转行则将一行数据拆分成多行。以下是如何在 MySQL 中实现这两种操作的详细解释。
1. 行转列(Pivot)
行转列是将表中的行数据转换成列形式。MySQL 本身没有直接的 PIVOT 操作,但可以通过 条件聚合(Conditional Aggregation) 和 CASE 语句 实现。
1.1 示例数据
假设有一个表 sales,记录了不同月份的销售情况,如下所示:
| product_id | month | sales |
|---|---|---|
| 1 | Jan | 100 |
| 1 | Feb | 150 |
| 1 | Mar | 120 |
| 2 | Jan | 200 |
| 2 | Feb | 180 |
| 2 | Mar | 220 |
我们希望将每个产品的每个月的销售数据行转列,如下所示:
| product_id | Jan_sales | Feb_sales | Mar_sales |
|---|---|---|---|
| 1 | 100 | 150 | 120 |
| 2 | 200 | 180 | 220 |
1.2 行转列的实现方式
可以使用 CASE 和聚合函数(如 SUM())来实现:
SELECT
product_id,
SUM(CASE WHEN month = 'Jan' THEN sales ELSE 0 END) AS Jan_sales,
SUM(CASE WHEN month = 'Feb' THEN sales ELSE 0 END) AS Feb_sales,
SUM(CASE WHEN month = 'Mar' THEN sales ELSE 0 END) AS Mar_sales
FROM
sales
GROUP BY
product_id;
解释:
CASE语句:根据不同的月份给出不同的销售值,如果是目标月份则返回sales值,否则返回0。SUM()聚合函数:聚合相同product_id下的销售数据。GROUP BY:按照product_id分组,生成每个产品的销售数据。
2. 列转行(Unpivot)
列转行是将列的数据转换成行。MySQL 可以通过 UNION ALL 来实现列转行操作。假设我们有以下表 sales,记录了每个月的销售数据:
| product_id | Jan_sales | Feb_sales | Mar_sales |
|---|---|---|---|
| 1 | 100 | 150 | 120 |
| 2 | 200 | 180 | 220 |
我们希望将每个月的销售数据列转行,结果如下:
| product_id | month | sales |
|---|---|---|
| 1 | Jan | 100 |
| 1 | Feb | 150 |
| 1 | Mar | 120 |
| 2 | Jan | 200 |
| 2 | Feb | 180 |
| 2 | Mar | 220 |
2.1 列转行的实现方式
可以使用 UNION ALL 来将每个月的数据从列转成行:
SELECT product_id, 'Jan' AS month, Jan_sales AS sales FROM sales
UNION ALL
SELECT product_id, 'Feb' AS month, Feb_sales AS sales FROM sales
UNION ALL
SELECT product_id, 'Mar' AS month, Mar_sales AS sales FROM sales;
解释:
- 使用
UNION ALL将每个月的销售数据提取出来,并为每个月的数据创建一个month列。 SELECT product_id, 'Jan' AS month, Jan_sales AS sales:这里将Jan_sales列的值提取出来,并且通过'Jan'给每一行指定月份。- 对
Feb_sales和Mar_sales使用相同的方法。
3. 使用动态 SQL 实现通用的行转列和列转行
对于动态的场景(例如表的列数或者行数不固定),需要使用动态 SQL 来生成查询语句。这里我们简单介绍一下如何使用 PREPARE 和 EXECUTE 来构建动态 SQL。
3.1 动态行转列
假设我们有多个产品和月份,但数量未知,可以通过动态 SQL 来生成行转列查询:
SET @sql = NULL;
SELECT GROUP_CONCAT(
DISTINCT CONCAT(
'SUM(CASE WHEN month = "', month, '" THEN sales ELSE 0 END) AS ', month, '_sales'
)
) INTO @sql
FROM sales;
SET @sql = CONCAT('SELECT product_id, ', @sql, ' FROM sales GROUP BY product_id');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
3.2 动态列转行
如果需要将列转行,同样可以通过 UNION ALL 和动态 SQL 实现:
SET @sql = NULL;
SELECT GROUP_CONCAT(
DISTINCT CONCAT(
'SELECT product_id, "', month, '" AS month, ', month, '_sales AS sales FROM sales'
)
) INTO @sql
FROM sales;
SET @sql = CONCAT(@sql, ';');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
4. 总结
- 行转列(Pivot):通过使用
CASE和聚合函数(如SUM())实现。 - 列转行(Unpivot):通过使用
UNION ALL将每一列数据转换成独立的行。 - 动态 SQL:对于列数或行数不固定的情况,可以使用动态 SQL 来构建查询。
这两种操作在数据报表生成、统计分析和数据处理过程中都非常常见。掌握了这些方法后,你就能轻松地在 MySQL 中进行复杂的数据变换和分析。
发表回复