【笔记】MySQL行转列函数

GROUP_CONCAT()函数

创建表person_info,并插入数据


CREATE TABLE `person_info` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) DEFAULT NULL,
  `family` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;


INSERT INTO niffler.person_info (name, family) VALUES('张三', '张三爸');
INSERT INTO niffler.person_info (name, family) VALUES('张三', '张三妈');
INSERT INTO niffler.person_info (name, family) VALUES('李四', '李四爸');
INSERT INTO niffler.person_info (name, family) VALUES('李四', '李四妈');
INSERT INTO niffler.person_info (name, family) VALUES('李四', '李四大哥');
INSERT INTO niffler.person_info (name, family) VALUES('王二', '王二爷爷');
INSERT INTO niffler.person_info (name, family) VALUES('王二', '王二姐姐');
id name family
1 张三 张三爸
2 张三 张三妈
3 李四 李四爸
4 李四 李四妈
5 李四 李四大哥
6 王二 王二爷爷
7 王二 王二姐姐

语法:

GROUP_CONCAT([DISTINCT] expr [,expr ...]
             [ORDER BY {unsigned_integer | col_name | expr}
                 [ASC | DESC] [,col_name ...]]
             [SEPARATOR str_val])

示例:

SELECT name, GROUP_CONCAT(family SEPARATOR '&') AS familys FROM person_info GROUP BY name;

结果:

【笔记】MySQL行转列函数_第1张图片
可以看看我上篇写的关于GROUP_CONCAT()的文章【笔记】MySQL数据库GROUP_CONCAT() 函数输出结果的长度限制

CASE函数

MySQL中的CASE表达式不是行转列函数。它是一种条件表达式,用于根据条件对数据进行选择、计算和转换。

然而,你可以使用CASE表达式来实现行转列的效果。通过在CASE表达式中定义不同的条件和相应的结果,你可以将行的数据按照不同的条件拆分到不同的列中。

创建表person_grade,并插入数据

CREATE TABLE `person_grade` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) DEFAULT NULL,
  `subject` varchar(100) DEFAULT NULL,
  `mark` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

INSERT INTO niffler.person_grade (name, subject, mark) VALUES('张三', '数学', 66);
INSERT INTO niffler.person_grade (name, subject, mark) VALUES('张三', '语文', 99);
INSERT INTO niffler.person_grade (name, subject, mark) VALUES('李四', '数学', 77);
INSERT INTO niffler.person_grade (name, subject, mark) VALUES('李四', '政治', 80);
id name subject mark
1 张三 数学 66
2 张三 语文 99
3 李四 数学 77
4 李四 政治 80

以subject列为表头,展示每个人的成绩

select name , 
	(case subject when '数学' then mark end) as '数学' ,
	(case subject when '语文' then mark end) as '语文' ,
	(case subject when '政治' then mark end) as '政治' 
from person_grade;

结果

【笔记】MySQL行转列函数_第2张图片
确实是按照subject列作为表头展示了成绩,但是每条成绩都占用一行,那么如何把同一个人的成绩都在一行展示呢?

select name , 
	MAX(case subject when '数学' then mark end) as '数学' ,
	MAX(case subject when '语文' then mark end) as '语文' ,
	MAX(case subject when '政治' then mark end) as '政治' 
from person_grade
group by name ;

结果

【笔记】MySQL行转列函数_第3张图片

上面的脚本,把MAX函数换成SUM函数,效果一样;除了CASE函数,也可使用IF函数实现行转列的效果。

GROUP_CONCAT()和CASE()结合实现动态行转列

通过CASE()函数的例子可以看到,表头行字段数学语文政治都是我们提前已经知晓并且手动指定的,那么如果我们事先不知道有哪些表头字段,怎么办呢?

可以通过下面的脚本实现:

SET @sql = NULL;
SELECT
  GROUP_CONCAT(DISTINCT
    CONCAT(
      'max(case when subject = ''',
      subject,
      ''' then mark end) ',
      subject
    )
  ) INTO @sql
FROM
  Meeting;
SET @sql = CONCAT('SELECT Meeting_id, ', @sql, ' 
                  FROM Meeting 
                   GROUP BY Meeting_id');

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

# 定义预处理语句
# PREPARE stmt_name FROM preparable_stmt;
# 执行预处理语句
# EXECUTE stmt_name [USING @var_name [, @var_name] ...];
# 删除(释放)定义
# {DEALLOCATE | DROP} PREPARE stmt_name;

先来看看这句的执行的效果:

SELECT
  GROUP_CONCAT(DISTINCT
    CONCAT(
      'max(case when subject = ''',
      subject,
      ''' then mark end) as ',
      subject
    )
  ) 
FROM
  Meeting;

执行结果:

max(case when subject = '政治' then mark end) as 政治,max(case when subject = '数学' then mark end) as 数学,max(case when subject = '语文' then mark end) as 语文

是不是已经看出来了,就是为了动态得到行,不是由我们手动指定的,而是通过脚本自动生成、拼接而来。

然后把拼接后的结果 INTO @sql

再通过

SET @sql = CONCAT('SELECT name, ', @sql, ' 
                  FROM person_grade 
                   GROUP BY name');

拼成完整的SQL

SELECT name,
	max(case when subject = '政治' then mark end) as 政治,
	max(case when subject = '数学' then mark end) as 数学,
	max(case when subject = '语文' then mark end) as 语文
FROM person_grade 
GROUP BY name

你可能感兴趣的:(MySQL,mysql,行转列)