一道关于学生选课的sql语句面试题【附答案】

题干

假设有一个张学生选课表(stud_subject),这个表有两个字段:姓名(name)和学科(subject),另假设班级学生姓名没有重名的,这个表的两个字段构成联合主键(name,subject)。建表和测试数据初始化语句如下:

create table stud_subject(
name varchar(50) not null,
subject varchar(50) not null
);
alter table stud_subject add primary key (name,  subject);
insert into stud_subject values ('张三','语文');
insert into stud_subject values ('张三','数学');
insert into stud_subject values ('张三','化学');
insert into stud_subject values ('李四','语文');
insert into stud_subject values ('李四','英语');
insert into stud_subject values ('王五','数学');
insert into stud_subject values ('王五','化学');
commit

问题:写一条sql语句选择所有的选了‘数学’这门课但是没有选‘语文’的同学姓名列表,对应上面的测试语句应该返回‘王五’这一名同学。要求能够给出多种写法,越多越好。

解答

所有的语句均在Oracle数据库中测试通过。

1. exists 或 in

这个是最容易想到的,exists

select a.name 
from stud_subject a
where a.subject='数学' and not exists (
    select b.* from stud_subject b 
    where b.name=a.name and b.subject='语文')

in

select a.name 
from stud_subject a
where a.subject='数学' and a.name not in (
     select b.name from stud_subject b where b.subject='语文')

2. left join

这个有一定的技巧,对于大数据量的表格来说这个应该是比较好优化的。

select a.name 
from ( select name from stud_subject where subject='数学') a 
  left join 
     ( select name from stud_subject where subject='语文') b 
   on (a.name =b.name)
 where b.name is null

3. 复合语句

和 exists 一样也是最容易想到的。复合语句有点流氓,我感觉它就是两条语句,哈哈。

select name from stud_subject where subject='数学'
minus 
select name from stud_subject where subject='语文'

4.交叉制表

这个有点像孔乙已茴香豆的“茴”的四种写法。

select a.name 
from (select t.name, sum(t.shuxue) as shuxue, sum(t.yuwen) as yuwen 
      from (select name , 
               case subject when '数学' then 1 else 0 end as shuxue,
  		       case subject when '语文' then 1 else 0 end as yuwen  
  		   from stud_subject) t 
  	  group by t.name ) a
where a.shuxue = 1 and a.yuwen = 0

简化一下:

select a.name 
from 
     (select name,
        sum(case subject when '数学' then 1 else 0 end) as shuxue,
  	    sum(case subject when '语文' then 1 else 0 end) as yuwen  
     from stud_subject 
     group by name ) a
where a.shuxue = 1 and a.yuwen = 0

5. having

把交叉制表的这个语句再简化一下,用having过滤:

select a.name 
from ( select name , decode(subject,'数学',1,0) as shuxue,
          decode(subject,'语文',1,0) as yuwen from stud_subject ) a
group by a.name
having sum(a.shuxue) = 1 and sum(a.yuwen) = 0

再变换一下,这样虽然更具技巧性,但是更加简洁。

select a.name 
from ( select name , decode(subject,'数学',2,'语文',1,0) as tag 
           from stud_subject ) a
group by a.name
having sum(a.tag) = 2

感想

本人一直从事面试工作,一般也不会文什么刁难的题目,只是碰到有ocp证书或者一直从事数据库开发的人员会问这个问题,但是比较遗憾的是超过3/4的人一个也想不出来,至今没有一个人说出两种以上的写法的人。作为一个软件开发人员基本功还是要练的。

猜你喜欢

转载自blog.csdn.net/code_fan/article/details/82976924