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中进行转列的几种方式的更多相关文章

  1. sql中--行处理数据的两种方式

    --创建待使用的表格CREATE TABLE Orders ( OrderID INT , CostValue DECIMAL(18, 2) );WITH cte_temp AS ( SELECT 1 ...

  2. Oracle字符串行拆分成列的三种方式

    Oracle字符串行拆分成列的三种方式 --muphy 开发过程中经常会遇到将前台多个值用逗号连接一同传递到后台查询,这个用逗号连接的字符串分隔的每个字符串分别对应Oracle数据库表的不同行. 如下 ...

  3. Jmeter中的参数化常用的几种方式

    Jmeter中的参数化常用的几种方式,这里讲一下前两个方式,最后一个在csv参数化里已详细讲解. 1.用户参数 2.函数助手 3.CSV Data Set Config  一.用户参数 位置:添加-前 ...

  4. c#ASP.NET中页面传值共有这么几种方式

    一.目前在ASP.NET中页面传值共有这么几种方式: 1.Response.Redirect("http://www.hao123.com",false); 目标页面和原页面可以在 ...

  5. SQL Server 2008 数据库同步的两种方式 (发布、订阅)

    参考转载: SQL Server 2008 数据库同步的两种方式 (发布.订阅) 使用Sqlserver事务发布实现数据同步

  6. strus2中获取表单数据 两种方式 属性驱动 和模型驱动

    strus2中获取表单数据 两种方式 属性驱动 和模型驱动 属性驱动 /** * 当前请求的action在栈顶,ss是栈顶的元素,所以可以利用setValue方法赋值 * 如果一个属性在对象栈,在页面 ...

  7. 怎样在Android开发中FPS游戏实现的两种方式比较

    怎样在Android开发中FPS游戏实现的两种方式比较 如何用Android平台开发FPS游戏,其实现过程有哪些方法,这些方法又有哪些不同的地方呢?首先让我们先了解下什么是FPS 英文名:FPS (F ...

  8. Struts2中访问web元素的四种方式

    Struts2中访问web元素的四种方式如下: 通过ActionContext来访问Map类型的request.session.application对象. 通过实现RequestAware.Sess ...

  9. HTML中设置背景图的两种方式

    HTML中设置背景图的两种方式 1.background    background:url(images/search.png) no-repeat top; 2.background-image ...

随机推荐

  1. 【转载】C#相等性比较

    本文阐述C#中相等性比较,其中主要集中在下面两个方面 ==和!=运算符,什么时候它们可以用于相等性比较,什么时候它们不适用,如果不使用,那么它们的替代方式是什么? 什么时候,需要自定一个类型的相等性比 ...

  2. js 监控浏览器关闭(完美兼容chrome & ie & fire fox)

    var UnloadConfirm = {}; UnloadConfirm.MSG_UNLOAD = "数据尚未保存,离开后可能会导致数据丢失\n\n您确定要离开吗?"; Unlo ...

  3. 鼠标滚轮实现图片的缩放-------Day79

    今天是7月的最后一天了,不得不说,我定下的七月份剩余几天的计划是完不成了.一则工作确实紧了些,再则没能处理好生活.工作和学习的节奏.这才是人生最大的课题吧.只是也还好.至少自己还在坚持着.事实上真的越 ...

  4. Java数据结构与算法之排序

    排序从大体上来讲,做了两件事情: 1.比較两个数据项: 2.交换两个数据项.或复制当中一项 一.冒泡排序 大O表示法:交换次数和比較次数都为O(N*N). 算法原理: 1.比較相邻的元素.假设第一个比 ...

  5. 数据结构与算法之贪心算法 C++实现

    1.基本思路:从问题的某一个初始解触发逐步逼近给定的目标,以尽可能快的求得更好的解. 当达到算法中某一步不能再继续前进时.就停止算法,给出近似值.也就是说贪心算法并不从总体最优考虑,它所作出的选择仅仅 ...

  6. 【linux驱动分析】之dm9000驱动分析(三):sk_buff结构分析

    [linux驱动分析]之dm9000驱动分析(一):dm9000原理及硬件分析 [linux驱动分析]之dm9000驱动分析(二):定义在板文件里的资源和设备以及几个宏 [linux驱动分析]之dm9 ...

  7. Hibernate中的merge使用详情解说

    merge的作用是:新new一个对象,如果该对象设置了ID,则这个对象就当作游离态处理:                                       当ID在数据库中不能找到时,用up ...

  8. 教你如何配置Ubuntu用于高效、高质量的发送邮件

    本文首发在: http://mengxi.me/how-to-setup-ubuntu-sendmail-to-deliver-email-fast-and-reliable/ 在网站上线后,经常会遇 ...

  9. ios 关于动画用法的总结

      #import "FirstVC.h" @implementation FirstVC /*     创建xib过程     1 创建xib(名字和类名相同)     2 文件 ...

  10. js中的逻辑与(&&)与逻辑或(||)

    var foo = 1; var bar = 0; var tar = false; var baz = 2; 一.js中的逻辑与(&&) 1.当第一个数为true时,返回第二个数: ...