SQL中进行转列的几种方式
SQL中进行转列
在很多笔试的程序员中会有很多写SQL的情况,其中很多时候会考察行转列。那么这个时候如果能写出来几种行转列的SQL,会给面试官留下比较好的印象。
以下是这次sql转换的表结构以及数据
数据准备
1、学生表
CREATE TABLE `student` (
`stuid` VARCHAR(16) NOT NULL COMMENT '学号',
`stunm` VARCHAR(20) NOT NULL COMMENT '学生姓名',
PRIMARY KEY (`stuid`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB;
2、课程表
CREATE TABLE `curriculum` (
`courseno` VARCHAR(20) NOT NULL,
`coursenm` VARCHAR(100) NOT NULL,
PRIMARY KEY (`courseno`)
)
COMMENT='课程表'
COLLATE='utf8_general_ci'
ENGINE=InnoDB;
3、成绩表
CREATE TABLE `score` (
`stuid` VARCHAR(16) NOT NULL,
`courseno` VARCHAR(20) NOT NULL,
`scores` FLOAT NULL DEFAULT NULL,
PRIMARY KEY (`stuid`, `courseno`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB;
4、基本数据
/*学生表数据*/
Insert Into student (stuid, stunm) Values('1001', '张三');
Insert Into student (stuid, stunm) Values('1002', '李四');
Insert Into student (stuid, stunm) Values('1003', '赵二');
Insert Into student (stuid, stunm) Values('1004', '王五');
Insert Into student (stuid, stunm) Values('1005', '刘青');
Insert Into student (stuid, stunm) Values('1006', '周明');
/*课程表数据*/
Insert Into curriculum (courseno, coursenm) Values('C001', '大学语文');
Insert Into curriculum (courseno, coursenm) Values('C002', '新视野英语');
Insert Into curriculum (courseno, coursenm) Values('C003', '离散数学');
Insert Into curriculum (courseno, coursenm) Values('C004', '概率论与数理统计');
Insert Into curriculum (courseno, coursenm) Values('C005', '线性代数');
Insert Into curriculum (courseno, coursenm) Values('C006', '高等数学(一)');
Insert Into curriculum (courseno, coursenm) Values('C007', '高等数学(二)');
/*成绩表数据*/
Insert Into number_result(stuid, courseno, scores) Values('1001', 'C001', 67);
Insert Into number_result(stuid, courseno, scores) Values('1002', 'C001', 68);
Insert Into number_result(stuid, courseno, scores) Values('1003', 'C001', 69);
Insert Into number_result(stuid, courseno, scores) Values('1004', 'C001', 70);
Insert Into number_result(stuid, courseno, scores) Values('1005', 'C001', 71);
Insert Into number_result(stuid, courseno, scores) Values('1006', 'C001', 72);
Insert Into number_result(stuid, courseno, scores) Values('1001', 'C002', 87);
Insert Into number_result(stuid, courseno, scores) Values('1002', 'C002', 88);
Insert Into number_result(stuid, courseno, scores) Values('1003', 'C002', 89);
Insert Into number_result(stuid, courseno, scores) Values('1004', 'C002', 90);
Insert Into number_result(stuid, courseno, scores) Values('1005', 'C002', 91);
Insert Into number_result(stuid, courseno, scores) Values('1006', 'C002', 92);
Insert Into number_result(stuid, courseno, scores) Values('1001', 'C003', 83);
Insert Into number_result(stuid, courseno, scores) Values('1002', 'C003', 84);
Insert Into number_result(stuid, courseno, scores) Values('1003', 'C003', 85);
Insert Into number_result(stuid, courseno, scores) Values('1004', 'C003', 86);
Insert Into number_result(stuid, courseno, scores) Values('1005', 'C003', 87);
Insert Into number_result(stuid, courseno, scores) Values('1006', 'C003', 88);
Insert Into number_result(stuid, courseno, scores) Values('1001', 'C004', 88);
Insert Into number_result(stuid, courseno, scores) Values('1002', 'C004', 89);
Insert Into number_result(stuid, courseno, scores) Values('1003', 'C004', 90);
Insert Into number_result(stuid, courseno, scores) Values('1004', 'C004', 91);
Insert Into number_result(stuid, courseno, scores) Values('1005', 'C004', 92);
Insert Into number_result(stuid, courseno, scores) Values('1006', 'C004', 93);
Insert Into number_result(stuid, courseno, scores) Values('1001', 'C005', 77);
Insert Into number_result(stuid, courseno, scores) Values('1002', 'C005', 78);
Insert Into number_result(stuid, courseno, scores) Values('1003', 'C005', 79);
Insert Into number_result(stuid, courseno, scores) Values('1004', 'C005', 80);
Insert Into number_result(stuid, courseno, scores) Values('1005', 'C005', 81);
Insert Into number_result(stuid, courseno, scores) Values('1006', 'C005', 82);
Insert Into number_result(stuid, courseno, scores) Values('1001', 'C006', 77);
Insert Into number_result(stuid, courseno, scores) Values('1002', 'C006', 78);
Insert Into number_result(stuid, courseno, scores) Values('1003', 'C006', 79);
Insert Into number_result(stuid, courseno, scores) Values('1004', 'C006', 80);
Insert Into number_result(stuid, courseno, scores) Values('1005', 'C006', 81);
Insert Into number_result(stuid, courseno, scores) Values('1006', 'C006', 82);
我们先看一下最基本的查询效果是什么样的
静态行转列
Select st.stuid, st.stunm,
MAX(CASE c.coursenm WHEN '大学语文' THEN s.scores ELSE 0 END ) '大学语文',
MAX(CASE c.coursenm WHEN '新视野英语' THEN ifnull(s.scores,0) ELSE 0 END ) '新视野英语',
MAX(CASE c.coursenm WHEN '离散数学' THEN ifnull(s.scores,0) ELSE 0 END ) '离散数学',
MAX(CASE c.coursenm WHEN '概率论与数理统计' THEN ifnull(s.scores,0) ELSE 0 END ) '概率论与数理统计',
MAX(CASE c.coursenm WHEN '线性代数' THEN ifnull(s.scores,0) ELSE 0 END ) '线性代数',
MAX(CASE c.coursenm WHEN '高等数学(一)' THEN ifnull(s.scores,0) ELSE 0 END ) '高等数学(一)',
MAX(CASE c.coursenm WHEN '高等数学(二)' THEN ifnull(s.scores,0) ELSE 0 END ) '高等数学(二)'
From student st
Left Join number_result s On st.stuid = s.stuid
Left Join curriculum c On c.courseno = s.courseno
Group by st.stuid
很多人肯定不理解为什么要使用Max函数,实际上大家都知道聚合函数是和分组进行搭配使用的。这一点毋庸置疑,那么大家可以把Max函数去掉看看会显示什么效果。切记去掉Max函数记得把分组也去掉,这样才能看到本质。
这时大家会发现没列都出现了重复的数据,而且只有一列是有值得。其他列都是0.那么这个时候就应该能很清楚的认识到,为什么使用Max函数了。在分组的同时取一组中的最大值。
静态行转列有一个弊端就是第一确定有多少个课程,然后再把课程名称拿出来再写查询语句。但是这样会写很多东西。
动态行转列
首先我们要动态的获取是列的数据 :
MAX(CASE c.coursenm WHEN ‘大学语文’ THEN s.scores ELSE 0 END ) ‘大学语文’,
MAX(CASE c.coursenm WHEN ‘线性代数’ THEN ifnull(s.scores,0) ELSE 0 END ) ‘线性代数’,
MAX(CASE c.coursenm WHEN ‘离散数学’ THEN ifnull(s.scores,0) ELSE 0 END ) ‘离散数学’
这里想动态的获取到上面的就需要拼接sql列 :
SELECT
GROUP_CONCAT( DISTINCT CONCAT( ‘MAX(IF(c.coursenm = ‘’’, c.coursenm, ‘’’, s.scores, 0)) AS ‘’’, c.coursenm, ‘’’’ ) )
FROM
curriculum c;
在这里解释一下 :
concat()函数 : 将多个字符串连接成一个字符串。
语法:concat_ws(separator, str1, str2, …)
说明:第一个参数指定分隔符。需要注意的是分隔符不能为null,如果为null,则返回结果为null。
group_concat()函数 :将group by产生的同一个分组中的值连接起来,返回一个字符串结果。
语法:group_concat( [distinct] 要连接的字段 [order by 排序字段 asc/desc ] [separator ‘分隔符’] )。
说明:通过使用distinct可以排除重复值;如果希望对结果中的值进行排序,可以使用order by子句;separator是一个字符串值,缺省为一个逗号。
动态的列是拿到了,那如何再结合SQL语句进行查询得到结果呢?
这里要说明一点,因为用到了拼接函数,如果像上面的查询语句,只是把那几行语句替换掉,也就是下面这样
Select st.stuid, st.stunm,
(
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'MAX(IF(c.coursenm = ''',
c.coursenm,
''', s.scores, NULL)) AS ',
c.coursenm
)
)
FROM curriculum c
)
From Student st
Left Join number_result s On st.stuid = s.stuid
Left Join curriculum c On c.courseno = s.courseno
Group by st.stuid;
最终结果如下 :
SET @SQL = NULL;
SELECT
GROUP_CONCAT( DISTINCT CONCAT( ‘MAX(IF(c.coursenm = ‘’’, c.coursenm, ‘’’, s.scores, 0)) AS ‘’’, c.coursenm, ‘’’’ ) ) INTO @SQL
FROM
curriculum c;
SET @SQL = CONCAT( 'Select st.stuid, st.stunm, ‘, @SQL, ’ From Student st
Left Join number_result s On st.stuid = s.stuid
Left Join curriculum c On c.courseno = s.courseno
Group by st.stuid’ );
PREPARE stmt
FROM
@SQL;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
这里简单说一下,先生命一个变量赋值为null,把拼接的查询sql赋值给声明的变量表中,也可以理解为生成一个临时表,把查询出来的数据放到临时表中。预定义一个语句,并将它赋给 stmt。
存储过程–动态行转列
用存储过程的好处是,方便我们调用,相当于一个函数,其他可能也是类似的查询不需再重复写代码,直接调存储过程就好,还能随心所欲的加上if条件判断。创建存储过程的语句我就不多写了,这里把上面的查询语句直接放到创建存储过程的begin和end直接就可以了,如下:
DELIMITER &&
drop procedure if exists SP_QueryData;
Create Procedure SP_QueryData(IN stuid varchar(16))
READS SQL DATA
BEGIN
SET @sql = NULL;
SET @stuid = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'MAX(IF(c.coursenm = ''',
c.coursenm,
''', s.scores, 0)) AS ''',
c.coursenm, '''
)
) INTO @sql
FROM curriculum c;
SET @sql = CONCAT('Select st.stuid, st.stunm, ', @sql,
' From student st
Left Join number_result s On st.stuid = s.stuid
Left Join curriculum c On c.courseno = s.courseno');
IF stuid is not null and stuid <> '' then
SET @stuid = stuid;
SET @sql = CONCAT(@sql, ' Where st.stuid = '', @stuid, ''');
END IF;
SET @sql = CONCAT(@sql, ' Group by st.stuid');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END &&
DELIMITER ;
SQL中进行转列的几种方式的更多相关文章
- sql中--行处理数据的两种方式
--创建待使用的表格CREATE TABLE Orders ( OrderID INT , CostValue DECIMAL(18, 2) );WITH cte_temp AS ( SELECT 1 ...
- Oracle字符串行拆分成列的三种方式
Oracle字符串行拆分成列的三种方式 --muphy 开发过程中经常会遇到将前台多个值用逗号连接一同传递到后台查询,这个用逗号连接的字符串分隔的每个字符串分别对应Oracle数据库表的不同行. 如下 ...
- Jmeter中的参数化常用的几种方式
Jmeter中的参数化常用的几种方式,这里讲一下前两个方式,最后一个在csv参数化里已详细讲解. 1.用户参数 2.函数助手 3.CSV Data Set Config 一.用户参数 位置:添加-前 ...
- c#ASP.NET中页面传值共有这么几种方式
一.目前在ASP.NET中页面传值共有这么几种方式: 1.Response.Redirect("http://www.hao123.com",false); 目标页面和原页面可以在 ...
- SQL Server 2008 数据库同步的两种方式 (发布、订阅)
参考转载: SQL Server 2008 数据库同步的两种方式 (发布.订阅) 使用Sqlserver事务发布实现数据同步
- strus2中获取表单数据 两种方式 属性驱动 和模型驱动
strus2中获取表单数据 两种方式 属性驱动 和模型驱动 属性驱动 /** * 当前请求的action在栈顶,ss是栈顶的元素,所以可以利用setValue方法赋值 * 如果一个属性在对象栈,在页面 ...
- 怎样在Android开发中FPS游戏实现的两种方式比较
怎样在Android开发中FPS游戏实现的两种方式比较 如何用Android平台开发FPS游戏,其实现过程有哪些方法,这些方法又有哪些不同的地方呢?首先让我们先了解下什么是FPS 英文名:FPS (F ...
- Struts2中访问web元素的四种方式
Struts2中访问web元素的四种方式如下: 通过ActionContext来访问Map类型的request.session.application对象. 通过实现RequestAware.Sess ...
- HTML中设置背景图的两种方式
HTML中设置背景图的两种方式 1.background background:url(images/search.png) no-repeat top; 2.background-image ...
随机推荐
- c/c++中static和extern使用
c/c++中static和extern使用 在C/C++中static和extern都能够用来修饰函数和变量,可是是有差别的. 内部函数和内部变量:仅仅能在文件内使用的函数和变量. 外部函数和外部变量 ...
- php 封装memcache类
<?php /* * memcache类 */ class Memcacheds{ //声明静态成员变量 private static $m = null; pri ...
- SGU 194 Reactor Cooling 无源汇带上下界可行流
Reactor Cooling time limit per test: 0.5 sec. memory limit per test: 65536 KB input: standard output ...
- Jenkins+maven+SVN+Tomcat部署过程
一.下载地址 应首先确认安装了JDK: Jenkins下载地址:http://mirrors.shu.edu.cn/jenkins/windows-stable/jenkins-2.107.3.zip ...
- SELECT INSTR(120,0000); 真
sql 排故 SELECT INSTR(120,0000); 真
- CH 5105 Cookies(贪心+DP)
\(CH 5105 Cookies\) \(solution:\) 真是好题一道!这道题我想了很久很久,就得这一题可以直接完全贪心,可惜最后还是失败了,但是对贪心的深入思考也换来了一个最优解方案.然后 ...
- JS中prototype,js原型扩展
作者:轩脉刃(yjf512)出处:(http://www.cnblogs.com/yjf512/)版权声明:本文的版权归作者与博客园共有.欢迎转载阅读,转载时须注明本文的详细链接. 原文 http:/ ...
- git apply、git am打补丁.diff 和 .patch【转】
本文转载自:https://www.jianshu.com/p/e5d801b936b6 前提: 生成patch: git format-patch -M master 生成指定patch,0163b ...
- atomic_cmpxchg()/Atomic_read()/Atomic_set()/Atomic_add()/Atomic_sub()/atomi
[ 1.atomic_read与atomic_set函数是原子变量的操作,就是原子读和原子设置的作用.2.原子操作,就是执行操作的时候,其数值不会被其它线程或者中断所影响3.原子操作是linux内核中 ...
- LoadRunner 技巧之 IP欺骗
IP欺骗也是也loadrunner自带的一个非常有用的功能. 需要使用ip欺骗的原因:1.当某个IP的访问过于频繁,或者访问量过大是,服务器会拒绝访问请求,这时候通过IP欺骗可以增加访问频率和访问量, ...