一道面试题引发的数据库行列转换实践

问题场景

最近有个朋友去面试,问了我一道面试题。题目如下,在形如下面的数据库表score中,找出每门成绩(grade)都大于等于80分的学生姓名。

----------------------------------------

name      | course  | grade

----------------------------------------

zhangsan  | Java   | 70

----------------------------------------

zhangsan  | C++    | 80

----------------------------------------

lisi     | Java   | 90

----------------------------------------

lisi     | C++    | 60

----------------------------------------

wangwu   | Java   | 85

----------------------------------------

wangwu   | C++   | 95

----------------------------------------

期望结果

----------------------------------------

name

----------------------------------------

wangwu

----------------------------------------

本文以MySQL数据库为例,以三种方案一步一步实现面试题要求。

方案一

1、寻求行列转换,也称矩阵转置,将多列数据转为一行,这一步最关键,实现SQL语句如下:

下面的sql是以score为主表来构建多列,构造的从表为每门课程的数据表,通过主表的 name 字段来关联从表的 name 字段。

select s.`name` as name,
(select grade from score where name = s.`name` and course = ‘Java‘)as ‘Java‘,
(select grade from score where name = s.`name` and course = ‘C++‘)as ‘C++‘
from score s

运行结果截图:

2、对于1的执行结果,需要过滤掉重复的行,只要通过 distinct 关键字就可以了,实现SQL语句如下:

select distinct s.`name` as name,#此处加distinct来过滤相同的行
(select grade from score where name = s.`name` and course = ‘Java‘)as ‘Java‘,
(select grade from score where name = s.`name` and course = ‘C++‘)as ‘C++‘
from score s

运行结果截图:

3、最后通过构造一个子查询,即是把上面2的查询结果作为一个表来查询,进行 where 行级过滤就可以了,实现SQL语句如下:

select *
from
(
    select distinct s.`name` as name,#此处加distinct来过滤相同的行
    (select grade from score where name = s.`name` and course = ‘Java‘)as G1,
    (select grade from score where name = s.`name` and course = ‘C++‘)as G2
    from score s
) score
where G1>=80 and G2>=80

运行结果截图:

问题:这里有一个问题指出下,如果写成如下的sql语句,把Java和C++作为列名的话(还有C#),查询结果为NULL,这个问题后续会详解,请见运行结果截图:

select *
from
(
    select distinct s.`name` as name,#此处加distinct来过滤相同的行
    (select grade from score where name = s.`name` and course = ‘Java‘)as ‘Java‘,
    (select grade from score where name = s.`name` and course = ‘C++‘)as ‘C++‘
    from score s
) score
where ‘Java‘>=80 and ‘C++‘>=80

运行结果截图:

方案二

1、通过group和聚合函数sum的结合使用,通过group by name分组,利用sum假模假样计算出每门课程的成绩,sum的时候利用case判断课程类别,就得到了以行显示的每个学生的每门课程成绩,这一步最关键,实现SQL语句如下:

select  name,
sum(case when course=‘Java‘ then grade end) as ‘Java‘,
sum(case when course=‘C++‘ then grade end) as ‘C++‘
from score group by name

运行结果截图:

2、再通过构造一个子查询,即是把上面1的查询结果作为一个表来查询,进行 where 行级过滤就可以了,实现SQL语句如下:

select *
from
(
select  name,
sum(case when course=‘Java‘ then grade end) as G1,
sum(case when course=‘C++‘ then grade end) as G2
from score group by name
) score
where G1>=80 and G2>=80

运行结果截图:

方案三

1、先找出有任意课程<80分的学生,实现SQL语句如下:

select  name
from score
where grade<80

运行结果截图:

2、distinct出所有学生列表(不重复),实现SQL语句如下:

select distinct name
from score

运行结果截图:

3、通过构造子查询从查询2的结果排除出去查询1的结果,这一步骤有的数据库是有集合函数,比如SQL Server的Except,这儿我们用not exists进行行级过滤,实现SQL语句如下:

select *
from
(
    select distinct name
    from score
) score1
where not exists
(
    select *
    from
    (
        select distinct name
        from score
        where grade<80
    ) score2
    where score1.name=score2.name
)

运行结果截图:

总结:

时间: 2024-10-06 09:11:04

一道面试题引发的数据库行列转换实践的相关文章

【转】腾讯高级工程师:一道面试题引发的高并发性能调试思考

https://dbaplus.cn/news-21-625-1.html 这样打破沙锅问到底的精神十分可贵!注意其中用到的工具 4月份的时候看到一道面试题,据说是腾讯校招面试官提的:在多线程和高并发环境下,如果有一个平均运行一百万次才出现一次的bug,你如何调试这个bug?(知乎原贴地址如下:https://www.zhihu.com/question/43416744) 遗憾的是知乎很多答案在抨击这道题本身的正确性,虽然我不是这次的面试官,但我认为这是一道非常好的面试题.当然,只是道加分题,

一道面试题引发的pythonic

一道测试工程师面试题(来自搜狗): 自己写了解法: # -*- coding: utf-8 -*- import re #从整体log中过滤出有用的部分,缩小搜索范围 def filter_log(the_log): r = r'[CRIUS]' return re.findall(r, the_log) #统计每个目标字符出现过的最少次数,即最少出现过几次完整log def check_count(target,target_log): target_dic = {} for one in t

(转)c++类的成员函数存储方式(是否属于类的对象)---一道面试题引发的思考

昨天去面试一家公司,面试题中有一个题,自己没弄清楚,先记录如下: class D { public: void printA() { cout<<"printA"<<endl; } virtual void printB() { cout<<"printB"<<endl; } }; main函数调用: D *d=NULL; d->printA(); d->printB(); 输出结果是? 当时想的是对象d直

一道面试题引发的对javascript类型转换的思考

最近群里有人发了下面这题:实现一个函数,运算结果可以满足如下预期结果: add(1)(2) // 3 add(1, 2, 3)(10) // 16 add(1)(2)(3)(4)(5) // 15 对于一个好奇的切图仔来说,忍不住动手尝试了一下,看到题目首先想到的是会用到高阶函数以及 Array.prototype.reduce(). 高阶函数(Higher-order function):高阶函数的意思是它接收另一个函数作为参数.在 javascript 中,函数是一等公民,允许函数作为参数或

数据库的行列转换问题

在平常的工作中或者面试中,我们可能有遇到过数据库的行列转换问题.今天我们就来讨论下. 1.创建表 首先我们来创建一张表. sql语句: --1. 创建数据表 if OBJECT_ID('Score') is not null drop table Score create table Score ( 姓名 nvarchar(128), 课程 nvarchar(128), 分数 int ) insert into Score values('张三','语文',98) insert into Sco

Mysql实现行列转换

前言: 最近又玩起了sql语句,想着想着便给自己出了一道题目:“行列转换”.起初瞎折腾了不少时间也上网参考了一些博文,不过大多数是采用oracle数据库当中的一些便捷函数进行处理,比如”pivot”.那么,在Mysql环境下如何处理? 自己举了个小例子: sql代码实现: 1 -- Step1:建表并插入数据 2 3 -- Step2:中间转换,即“二维转一维”得到一维表. 4 5 -- Step3:利用IF判断并“group by”即可得到目标结果表. 6 7 8 9 -- 加载 10 11

SQL行列转换6种方法

在进行报表开发时,很多时候会遇到行列转换操作,很对开发人员针对于SQL级别行列转换操作一直不甚理解,今天正好抽空对其进行了一些简单的总结.这里主要列举3种可以实现SQL行列转换的方法,包括通用SQL解法以及Oracle支持解法. 一.测试数据 测试表依旧采用Oracle经典的scott模式下的dept表和emp表,结构如下: DEPT: create table DEPT ( DEPTNO NUMBER(2) not null, DNAME VARCHAR2(14), LOC VARCHAR2(

华为的一道机试题--等式变换

华为的一道机试题 (http://blog.csdn.net/zombie_slicer/article/details/37346025) 第三题:等式变换 输入一个正整数X,在下面的等式左边的数字之间添加+号或者-号,使得等式成立. 1 2 3 4 5 6 7 8 9 = X 比如: 12-34+5-67+89 = 5 1+23+4-5+6-7-8-9 = 5 请编写程序,统计满足输入整数的所有整数个数. 输入:       正整数,等式右边的数字 输出:       使该等式成立的个数 样

锋利的SQL-SQL Server的表旋转(行列转换)

所谓表旋转,就是将表的行转换为列,或是将表的列转换为行,这是从SQL Server 2005开始提供的新技术.因此,如果希望使用此功能,需要将数据库的兼容级别设置为90.表旋转在某些方面也是解决了表的数据存储和实际需要之间的矛盾.例如,图9-4所示的是一个典型的产品销售统计表,这种格式虽然便于阅读,但是在进行数据表存储的时候却并不容易管理,产品销售数据表通常需要设计成图9-5所示的结构.这样就带来一个问题,用户既希望数据容易管理,又希望能够生成一种能够容易阅读的表格数据,这时候就可以使用表旋转技