Mysql 根据条件合并行数据后再根据条件行转列

背景

因公司要求,现需要对用户填写的问卷调查的数据进行统计,最终呈现样式为EXCEL表格,内容为用户填写的每个问题的选项,即如下表格样式:

用户身份证号 问题1 问题2 问题3 问题22
110000190001010011 1 2 1,4 1
370000199002021002 3 1 2,5 1,3,4

先介绍一下相关的表:
1. 问卷调查问题表:topic_question(保存所有问卷调查的问题,本案例中未用到仅提及不过多展示字段)
2. 问卷调查选项表:topic_option(保存所有问题的选项)

字段名 字段含义
id 自增主键
question_id 问题id
option 选项文字描述
order 针对每个问题的选项序号
use_flag 是否启用
  1. 问卷调查用户填写表:topic_answer(保存所有用户的填写情况,一个用户填写的一个问题的一个选项就是一行数据
字段名 字段含义
id 自增主键
question_id 问题id(topic_question.id)
option_id 选项id表主键(topic_option.id 非对应问题的选项id)
user_id 用户表id
task_id 任务id
  1. 用户表(user_info)
字段名 字段含义
id 自增主键
id_card 身份证号

sql编写逻辑梳理:

  1. 因为最终输出的结果是根据身份证号码展示的所以需要根据填写问卷调查的用户id查询出对应的身份证号码;
  2. 因为问题是多选的,要在同一个单元格中显示多个选项,需要先将用户填写同一个问题的选项进行合并;
  3. 因为topic_answer表中存储的是一个用户一个问题的一个选项就是一行数据,所以需要将用户填写的所有问题选项根据问题编号进行行转列;
  4. 因为topic_answer表的option_id字段不是选项的序号,所以还需要进行联合查询出对应的选项序号;

所以当前面临的问题:

  1. 如何将同一个用户的同一个问题的选项进行合并,即标题中提及的合并行数据;
  2. 如何将同一个用户的多个问题横向展示:每个用户是一行数据,除第一列是身份证号外,剩余每一列为每个问题的选项,即标题中的行转列;

sql编写:

有了上面的逻辑流程和问题就好说了,于是查找合并行相关的博客之后写出了第一版sql,这一版sql可以将多行的数据根据问题id将选项进行合并,即标题中的根据条件合并行数据,多行转一行
参考博客:mysql中将多行数据合并成一行数据

SELECT 
	USER.id_card,
	answer.question_id,
	GROUP_CONCAT( answer.option_id SEPARATOR ',' ) AS option_id 
FROM
	topic_answer answer
	LEFT JOIN user_info USER ON answer.user_id = USER.id 
GROUP BY
	USER.id_card,
	answer.question_id;

之后,又查阅了相关的文档,写出了第二版sql,该版距离目标基本成型,但是中间却踩了很多坑,耗费时间相对较长,后面再说需要主意的地方;不过还是完成了行转列,主要就是运用了GROUP_CONCAT和CASE…WHEN…THEN方法。
参考博客:mysql 行转列,多行转一行,列转行,一行转多列

SELECT
	answer2.id_card '身份证号码',
	GROUP_CONCAT( CASE answer2.question_id WHEN 1 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题1',
	GROUP_CONCAT( CASE answer2.question_id WHEN 2 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题2',
	GROUP_CONCAT( CASE answer2.question_id WHEN 3 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题3',
	GROUP_CONCAT( CASE answer2.question_id WHEN 4 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题4',
	GROUP_CONCAT( CASE answer2.question_id WHEN 5 THEN CAST(answer2.option_id AS char) ELSE NULL END ) '问题5',
	GROUP_CONCAT( CASE answer2.question_id WHEN 6 THEN CAST(answer2.option_id AS char) ELSE NULL END ) '问题6',
	GROUP_CONCAT( CASE answer2.question_id WHEN 7 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题7',
	GROUP_CONCAT( CASE answer2.question_id WHEN 8 THEN CAST(answer2.option_id AS char) ELSE NULL END ) '问题8',
	GROUP_CONCAT( CASE answer2.question_id WHEN 9 THEN CAST(answer2.option_id AS char) ELSE NULL END ) '问题9',
	GROUP_CONCAT( CASE answer2.question_id WHEN 10 THEN CAST(answer2.option_id AS char) ELSE NULL END ) '问题10',
	GROUP_CONCAT( CASE answer2.question_id WHEN 11 THEN CAST(answer2.option_id AS char) ELSE NULL END ) '问题11',
	GROUP_CONCAT( CASE answer2.question_id WHEN 12 THEN CAST(answer2.option_id AS char) ELSE NULL END ) '问题12',
	GROUP_CONCAT( CASE answer2.question_id WHEN 13 THEN CAST(answer2.option_id AS char) ELSE NULL END ) '问题13',
	GROUP_CONCAT( CASE answer2.question_id WHEN 14 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题14',
	GROUP_CONCAT( CASE answer2.question_id WHEN 15 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题15',
	GROUP_CONCAT( CASE answer2.question_id WHEN 16 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题16',
	GROUP_CONCAT( CASE answer2.question_id WHEN 17 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题17',
	GROUP_CONCAT( CASE answer2.question_id WHEN 18 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题18',
	GROUP_CONCAT( CASE answer2.question_id WHEN 19 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题1',
	GROUP_CONCAT( CASE answer2.question_id WHEN 20 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题20',
	GROUP_CONCAT( CASE answer2.question_id WHEN 21 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题21',
	GROUP_CONCAT( CASE answer2.question_id WHEN 22 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题22' 
FROM
	(
	SELECT 
		DISTINCT(user_info.id_card) AS id_card,
		answer.question_id AS question_id,
		GROUP_CONCAT(answer.option_id SEPARATOR ',' ) AS option_id 
	FROM
		topic_answer answer
		LEFT JOIN user_info user_info ON answer.user_id = user_info.id 
	GROUP BY
		user_info.id_card,
		answer.question_id 
	) as answer2
GROUP BY
	answer2.id_card;

然后需要将选项id融入到这里面,让页面展示的时候是每个问题对应的选项id,而不是选项表的主键,所以出了第三版sql:

SELECT
	answer2.id_card '身份证号码',
	GROUP_CONCAT( CASE answer2.question_id WHEN 1 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题1',
	GROUP_CONCAT( CASE answer2.question_id WHEN 2 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题2',
	GROUP_CONCAT( CASE answer2.question_id WHEN 3 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题3',
	GROUP_CONCAT( CASE answer2.question_id WHEN 4 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题4',
	GROUP_CONCAT( CASE answer2.question_id WHEN 5 THEN CAST(answer2.option_id AS char) ELSE NULL END ) '问题5',
	GROUP_CONCAT( CASE answer2.question_id WHEN 6 THEN CAST(answer2.option_id AS char) ELSE NULL END ) '问题6',
	GROUP_CONCAT( CASE answer2.question_id WHEN 7 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题7',
	GROUP_CONCAT( CASE answer2.question_id WHEN 8 THEN CAST(answer2.option_id AS char) ELSE NULL END ) '问题8',
	GROUP_CONCAT( CASE answer2.question_id WHEN 9 THEN CAST(answer2.option_id AS char) ELSE NULL END ) '问题9',
	GROUP_CONCAT( CASE answer2.question_id WHEN 10 THEN CAST(answer2.option_id AS char) ELSE NULL END ) '问题10',
	GROUP_CONCAT( CASE answer2.question_id WHEN 11 THEN CAST(answer2.option_id AS char) ELSE NULL END ) '问题11',
	GROUP_CONCAT( CASE answer2.question_id WHEN 12 THEN CAST(answer2.option_id AS char) ELSE NULL END ) '问题12',
	GROUP_CONCAT( CASE answer2.question_id WHEN 13 THEN CAST(answer2.option_id AS char) ELSE NULL END ) '问题13',
	GROUP_CONCAT( CASE answer2.question_id WHEN 14 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题14',
	GROUP_CONCAT( CASE answer2.question_id WHEN 15 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题15',
	GROUP_CONCAT( CASE answer2.question_id WHEN 16 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题16',
	GROUP_CONCAT( CASE answer2.question_id WHEN 17 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题17',
	GROUP_CONCAT( CASE answer2.question_id WHEN 18 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题18',
	GROUP_CONCAT( CASE answer2.question_id WHEN 19 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题1',
	GROUP_CONCAT( CASE answer2.question_id WHEN 20 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题20',
	GROUP_CONCAT( CASE answer2.question_id WHEN 21 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题21',
	GROUP_CONCAT( CASE answer2.question_id WHEN 22 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题22' 
FROM
	(
	SELECT 
		DISTINCT(user_info.id_card) AS id_card,
		answer.question_id AS question_id,
		GROUP_CONCAT(answer.option_id SEPARATOR ',' ) AS option_id 	
	FROM
		topic_answer answer
		LEFT JOIN user_info ON answer.user_id = user_info.id 
		LEFT JOIN topic_option on answer.option_id = topic_option.id
	GROUP BY
		user_info.id_card,
		answer.question_id
	ORDER BY answer.question_id
	) as answer2
GROUP BY
	answer2.id_card;

最后,再将结果进行完善,第一个是每一个问题的选项结果进行排序,第二个是因为有的用户填写了多次所以需要去重,主要使用的是GROUP_CONCAT(distinct XXX order by XXX),因为默认的就是使用逗号进行分割,所以我将GROUP_CONCAT里面的SEPARATOR删除了,此次为最终版(因为现在表中只有这一次问卷调查,所以条件中没有对task_id进行筛选);
参考博客:group_concat你所不知道的功能排序,去重

SELECT
	answer2.id_card '身份证号码',
	GROUP_CONCAT( CASE answer2.question_id WHEN 1 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题1',
	GROUP_CONCAT( CASE answer2.question_id WHEN 2 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题2',
	GROUP_CONCAT( CASE answer2.question_id WHEN 3 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题3',
	GROUP_CONCAT( CASE answer2.question_id WHEN 4 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题4',
	GROUP_CONCAT( CASE answer2.question_id WHEN 5 THEN CAST(answer2.option_id AS char) ELSE NULL END ) '问题5',
	GROUP_CONCAT( CASE answer2.question_id WHEN 6 THEN CAST(answer2.option_id AS char) ELSE NULL END ) '问题6',
	GROUP_CONCAT( CASE answer2.question_id WHEN 7 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题7',
	GROUP_CONCAT( CASE answer2.question_id WHEN 8 THEN CAST(answer2.option_id AS char) ELSE NULL END ) '问题8',
	GROUP_CONCAT( CASE answer2.question_id WHEN 9 THEN CAST(answer2.option_id AS char) ELSE NULL END ) '问题9',
	GROUP_CONCAT( CASE answer2.question_id WHEN 10 THEN CAST(answer2.option_id AS char) ELSE NULL END ) '问题10',
	GROUP_CONCAT( CASE answer2.question_id WHEN 11 THEN CAST(answer2.option_id AS char) ELSE NULL END ) '问题11',
	GROUP_CONCAT( CASE answer2.question_id WHEN 12 THEN CAST(answer2.option_id AS char) ELSE NULL END ) '问题12',
	GROUP_CONCAT( CASE answer2.question_id WHEN 13 THEN CAST(answer2.option_id AS char) ELSE NULL END ) '问题13',
	GROUP_CONCAT( CASE answer2.question_id WHEN 14 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题14',
	GROUP_CONCAT( CASE answer2.question_id WHEN 15 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题15',
	GROUP_CONCAT( CASE answer2.question_id WHEN 16 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题16',
	GROUP_CONCAT( CASE answer2.question_id WHEN 17 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题17',
	GROUP_CONCAT( CASE answer2.question_id WHEN 18 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题18',
	GROUP_CONCAT( CASE answer2.question_id WHEN 19 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题1',
	GROUP_CONCAT( CASE answer2.question_id WHEN 20 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题20',
	GROUP_CONCAT( CASE answer2.question_id WHEN 21 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题21',
	GROUP_CONCAT( CASE answer2.question_id WHEN 22 THEN CAST(answer2.option_id AS char) ELSE NULL END) '问题22' 
FROM
	(
	SELECT 
		DISTINCT(user_info.id_card) AS id_card,
		answer.question_id AS question_id,
		GROUP_CONCAT(distinct topic_option.order order by topic_option.order) AS option_id	
	FROM
		topic_answer answer
		LEFT JOIN user_info ON answer.user_id = user_info.id 
		LEFT JOIN topic_option on answer.option_id = topic_option.id
	GROUP BY
		user_info.id_card,
		answer.question_id
	ORDER BY answer.question_id
	) as answer2
GROUP BY
	answer2.id_card;

最后再总结一下学到的知识及踩到的坑:

第一个学到的也是最主要的,就是拿到这个问题时,在没有头绪的情况下,如何才能梳理出解决问题的逻辑,这个主要是对问题进行拆解,大家可以先整理出思路再一步一步去解决,去查阅相关的资料,同时自主学习的能力也是很重要的,不要遇到问题就只想跟别人请教,先自己动脑想清楚再下手去做。然后就是GROUP_CONCAT方法的用法,再结合CASE…WHEN…THEN,distinct,order如何使用;
踩到的坑其实就是我自己的粗心,option_id字段本来是数字,但是多行数据合并成一行之后就变成了char类型,所以最外层由SUM(CASE answer2.question_id WHEN 1 THEN answer2.question_id else 0 end) 1 改成了 GROUP_CONCAT( CASE answer2.question_id WHEN 1 THEN CAST(answer2.option_id AS char) ELSE NULL END) 1 , 本以为这样就可以了,但是一直再报sql语句的错误,定位了好久才定位出来 使用了GROUP_CONCAT之后就变成了字符串,所以不能用数字展示到标题栏上,遂改为 GROUP_CONCAT( CASE answer2.question_id WHEN 1 THEN CAST(answer2.option_id AS char) ELSE NULL END) ‘问题1’,使用的时候还是需要特别注意的。

好了,这个问题就先写到这里,因为好久没写稍微复杂点的sql了,而且之前没用过GROUP_CONCAT方法,所以想着把这次记录一下,如果可以同时帮到你当然是十分荣幸的,同样如果对最终版的sql有什么建议或者可以优化的地方欢迎评论区留下你的想法~

你可能感兴趣的:(学习笔记,mysql,数据库,sql,java,spring)