在MySQL中实现交叉表查询2(动态交叉表)

在MySQL中实现交叉表查询2(动态交叉表)

交叉表分为静态交叉表和动态交叉表。其中静态交叉表中的列是固定的,因此相对容易实现;而动态交叉表中的列需要动态生成。

一、静态交叉表的实现

参见上一篇文章:在MySQL中实现交叉表查询1(静态交叉表)
https://blog.csdn.net/weixin_44377973/article/details/103099573。

二、动态交叉表的实现

创建三张表:学生表、课程表、成绩表

create table student(
    s_id char(10) comment '学号',
    s_name char(20) not null default '' comment '姓名',
    primary key(s_id)
);

create table course(
    c_id char(5),
    c_name char(20) not null default '' comment '课程名',
    primary key(c_id)
);

create table score(
    s_id char(10),
    c_id char(5),
    score int not null default 0,
    primary key(s_id,c_id)
);

为以上三张表添加数据:

Insert Into student(s_id,s_name) 
Values('2018001001','张小明'),('2018001002', '李小四'),('2018001003','赵小二'),
('2018001004','王小五'),('2018001005','刘长江'),('2018001006','周光明');

Insert Into course(c_id,c_name) 
Values('C0001','数据库'),('C0002','数据结构'),('C0003','高等数学'),('C0004','市场营销'),
('C0005','电子商务'),('C0006','网络营销'),('C0007','物流管理');

Insert Into score(s_id,c_id,score)
Values('2018001001','C0001',88),('2018001002', 'C0001',76),('2018001003','C0001',62),
('2018001004','C0001',74),('2018001005','C0001',61),('2018001006','C0001',58),
('2018001001','C0002',66),('2018001002','C0002',54),('2018001003','C0002',72),
('2018001004','C0002',69),('2018001005','C0002',65),('2018001006','C0002',70),
('2018001001','C0003',83),('2018001002','C0003',68),('2018001003','C0003',85),
('2018001004','C0003',75),('2018001005','C0003',63),('2018001006','C0003',42),
('2018001001','C0004',28),('2018001002','C0004',99),('2018001003','C0004',92),
('2018001004','C0004',91),('2018001005','C0004',74),('2018001006','C0004',88),
('2018001001','C0005',77),('2018001002','C0005',66),('2018001003','C0005',70),
('2018001004','C0005',80),('2018001005','C0005',69),('2018001006','C0005',82),
('2018001001','C0006',64),('2018001002','C0006',62),('2018001003','C0006',66),
('2018001004','C0006',80),('2018001005','C0006',71),('2018001006','C0006',60);

第一步: 动态获取和静态交叉表相似的语句

select IFNULL(s_name,'总分') as '姓名',
sum(if(c_name='数据库',score,0)) as '数据库',sum(if(c_name='数据结构',score,0)) as '数据结构',
sum(if(c_name='高等数学',score,0)) as '高等数学',sum(if(c_name='市场营销',score,0)) as '市场营销',
sum(if(c_name='电子商务',score,0)) as '电子商务',sum(if(c_name='网络营销',score,0)) as '网络营销',
sum(score) as '总分'
from student s,course c,score sc where s.s_id=sc.s_id and c.c_id=sc.c_id
group by s_name
with rollup;

如果要动态生成以上语句,需要使用SQL语句拼接。命令如下:

select group_concat(distinct concat('sum(if(c.c_name=\'',c_name),'\',sc.score,0)) as \'',c.c_name,'\'')
from student s,course c,score sc where s.s_id=sc.s_id and c.c_id=sc.c_id
;

以上命令的查询结果为:

sum(if(c_name='市场营销',score,0)) as '市场营销',sum(if(c_name='数据库',score,0)) as '数据库',sum(if(c_name='数据结构',score,0)) as '数据结构',sum(if(c_name='电子商务',score,0)) as '电子商务',sum(if(c_name='网络营销',score,0)) as '网络营销',sum(if(c_name='高等数学',score,0)) as '高等数学'

这样就不用考虑具体的课程门数以及课程的名称,代码就可以固定下来。

第二步: 拼接完整的SQL语句

set @sql=null;

select group_concat(distinct concat('sum(if(c_name=\'',c_name),'\',score,0)) as \'',c_name,'\'')
into @sql
from student s,course c,score sc where s.s_id=sc.s_id and c.c_id=sc.c_id;

set @sql=concat('select IFNULL(s_name,\'总分\') as \'姓名\',',@sql,',sum(score) as \'总分\' 
                from student s,course c,score sc where s.s_id=sc.s_id and c.c_id=sc.c_id 
                group by s_name with rollup;');

prepare stmt from @sql;
execute stmt;
deallocate prepare stmt;

group_concat的用法请参见我的个人博客:MySQL中group_concat函数用法总结,查询结果如下:

mysql> execute stmt;
+-----------+--------------+-----------+--------------+--------------+--------------+--------------+--------+
| 姓名      | 市场营销     | 数据库    | 数据结构     | 电子商务     | 网络营销     | 高等数学     | 总分   |
+-----------+--------------+-----------+--------------+--------------+--------------+--------------+--------+
| 刘长江    |           74 |        61 |           65 |           69 |           71 |           63 |    403 |
| 周光明    |           88 |        58 |           70 |           82 |           60 |           42 |    400 |
| 张小明    |           28 |        88 |           66 |           77 |           64 |           83 |    406 |
| 李小四    |           99 |        76 |           54 |           66 |           62 |           68 |    425 |
| 王小五    |           91 |        74 |           69 |           80 |           80 |           75 |    469 |
| 赵小二    |           92 |        62 |           72 |           70 |           66 |           85 |    447 |
| 总分      |          472 |       419 |          396 |          444 |          403 |          416 |   2550 |
+-----------+--------------+-----------+--------------+--------------+--------------+--------------+--------+
7 rows in set (0.01 sec)

prepare语句语法说明:

prepare statement_name from SQL字符串;
excute statement_name; --执行预处理语句
deallocate | drop prepare statement_name; --删除定义

第三步:创建存储过程

如果直接在MySQL中操作,到第二步就可以了,但如果在JAVA或PHP等项目中使用,就会出现问题,此时需要创建存储过程。创建存储过程时只需要把第二步生成的代码直接放到存储过程的begin和end之间就可以了,代码如下:

drop procedure if exists sp_query_crosstable;
delimiter && 

create procedure sp_query_crosstable() reads SQL data 
begin
set @sql=null;
select group_concat(distinct concat('sum(if(c_name=\'',c_name),'\',score,0)) as \'',c_name,'\'')
into @sql
from student s,course c,score sc where s.s_id=sc.s_id and c.c_id=sc.c_id;
set @sql=concat('select IFNULL(s_name,\'总分\') as \'姓名\',',@sql,',sum(score) as \'总分\' 
                from student s,course c,score sc where s.s_id=sc.s_id and c.c_id=sc.c_id 
                group by s_name with rollup;');
prepare stmt from @sql;
execute stmt;
deallocate prepare stmt;
end &&

delimiter ;

调用存储过程:

call sp_query_crosstable;
发布了44 篇原创文章 · 获赞 48 · 访问量 5419

猜你喜欢

转载自blog.csdn.net/weixin_44377973/article/details/103104537