MSSQL综合技术补习---常见笔试题1

为什么写这篇文章

 

本人最近在找工作,各种面试笔试,很多知识点久了都快忘记了,特别SQL也很久没有写了。

为了快速系统的补习下SQL相关的知识,决定写“MSSQL综合技术补习”系列文章。开始几篇为常见sql面试题型,然后慢慢转向系统的sql知识。

 

考点1---分组聚合条件统计(透视转换)

 

这样的题先分组,然后根据条件用case语句来区分条件,最后再聚合条件列。

(虽然透视转换可以使用SQL内置的Pivoting来实现,但是我觉得死语法没有自己写那样思路清楚,所以我没有使用内置的透视转换)

透视转换的步骤:

 

3个逻辑处理阶段:1.分组  2.扩展  3.聚合

 

 

例题1

2005-05-09

2005-05-09

2005-05-09

2005-05-09

2005-05-10

2005-05-10

2005-05-10

如果要生成下列结果, 该如何写sql语句?

 

 

时间         

2005-05-09

2

2

2005-05-10

1

2

 

 

分析:先根据rq分组,然后根据shengfu扩展,最后再根据shengfu聚合

 

select 
rq as 日期,
sum(case shengfu when '胜' then 1 else 0 end) as 胜,
sum(case shengfu when '负' then 1 else 0 end) as 负
from tmp
group by rq
复制代码

 

分辨使用透视转换的场景!

1.数据行是否转换成列,例如胜负是shengfu这个列的2个状态,而且展示结果胜负变成了2个列。

2.转换结果列的数据行是否是聚合结果。

如果上面2个条件都符合,那么就可以使用透视转换来做!

 

 

下面也是网上经常出现的sql面试题,我们用他来分析

 

table1

月份mon

部门dep

业绩yj

一月份

01

10

一月份

02

10

一月份

03

5

二月份

02

8

二月份

04

9

三月份

03

8

 

table2

部门dep

部门名称dname

01

国内业务一部

02

国内业务二部

03

国内业务三部

04

国际业务部

          

请用sql得到下面的结果

table3 (result)

部门dep

一月份

二月份

三月份

01

10

null

null

02

10

8

null

03

null

5

8

04

null

null

9

 

 

分析:table1中的mon列的数据1月份,2月份,3月份在table3里面变成了列,而这些列的数据是table1的yj列的sum聚合,所以这里只用透视转换

 

select a.dep,
sum(case when b.mon=1 then b.yj else 0 end) as '一月份',
sum(case when b.mon=2 then b.yj else 0 end) as '二月份',
sum(case when b.mon=3 then b.yj else 0 end) as '三月份'
from table2 a left join table1 b on a.dep=b.dep
复制代码

 

注意下这里是left join,因为你不可能知道部门的所有id,所以用部门的表去left join业绩表。

 

另外一个重点

就是在聚合数据的时候,需要考虑聚合数据是否存在NULL,如果存在,为了防止数字和NULL计算结果为NULL,应该使用ISNULL函数进行检查。

 

select 1+null
select sum(1+null)
--结果为NULL
select 1+isnull(null,0)
select sum(1+isnull(null,0))
复制代码

 

 

考点2---求符合聚合结果的数据(HAVING)

 

这样的题出现在面试题中的机会是很大的,题意的答案几乎都是先分组聚合,再在分组集上使用Having寻找符合题意的数据。

下面给出个最经典的题目。

为管理岗位业务培训信息,建立3个表:

 

S-学员表

S#-学号

SN-学员姓名

SD-所属单位

SA-学员年龄

 

C-课程表

C#-课程编号

CN-课程名称

 

SC-成绩表

S#-学号

C#-课程编号

G-学习成绩

 

要求:

使用标准SQL嵌套语句查询选修全部课程的学员姓名和所属单位

做这样的题目需要对Group by 和having有比较好的理解。

 

创建表和插入数据的sql

 

创建表

 

分析:

实际应用当中我们一般是不知道状态表会有多少数据的,也就是课程表,所以课程表的课程总数是应该在sql查询中去得到,然后再结合group by 和having来实现解这种类型题的经典解法。

 

 

select * from S 
where S# in
(
select SC.S# from SC right join C --注意这里能确定数据的表是课程表,所以用课程表来做连接主表
on SC.C#=C.C# --课程ID是连接的条件
group by SC.S# --根据学生ID分组
having count(distinct(SC.C#)) --注意一个同学可能存在一门课多个成绩
=(select count(C#) from C)
)
复制代码

 

另外几个相关题目

 

列出有二门以上(含两门)不及格课程的学生姓名及其平均成绩   

 

select S.S#,S.SN,SC1.SG from S join 
(
select S#,avg(G) as SG from SC
where G<60 group by S#
having count(distinct C#)>=2
) as SC1
on S.S#=SC1.S#
复制代码

 

列出既学过“数学”号课程,又学过“英语”号课程的所有学生姓名   

 

select S.S# from S where S# in 
(
select SC.S# from SC join C on SC.C#=C.C#
where C.CN in ('数学','英语')
group by SC.S#
having count(distinct C.CN)=2
)
复制代码

 

列出“1”号课成绩比“2”号课成绩高的所有学生的学号及其“1”号课和“2”号课的成绩

 

--这里要考虑到实际应用中可能出现一门课程多条成绩,所以取最高的成绩比较
select t1.S#,t1.CN,t1.G,t2.CN,t2.G from
(select top 1 SC1.S#,C1.CN,SC1.G from SC as SC1 join C as C1
on SC1.C#=C1.C# where C1.CN='英语' order by SC1.G desc)
as t1
join
(select top 1 SC2.S#,C2.CN,SC2.G from SC as SC2 join C as C2
on SC2.C#=C2.C# where C2.CN='数学' order by SC2.G desc)
as t2
on t1.S#=t2.S# where t1.G>t2.G
复制代码

 

 

考点3 取出表中的第31条到40条记录

 

取出表A中的第31条到40条记录,ID这里肯定是被破坏了的,不可能让你那么容易的得到。

这里主要是考你分页的知识点,不过比较简单,不多说了。

 

创建表

 

注意:虽然这里我们表中有ID和number这2个int字段,但是都是不完整的!而且题目不会让你怎么简单的去依靠连续体字段!

 

--错误的方案-注意order by
select top 10 * from
(
select top 40 * from test1
order by id desc
) as t order by t.id asc

--正确的方案1-结果降序
select top 10 * from
(
select top 40 * from test1
order by id asc
) as t order by t.id desc

--正确的方案2-结果升序
select top 10 * from test1
where id not in
(
select top 30 id from test1
order by id asc
) order by id asc
复制代码

 

这里的第一个错误解决方案是因为最后一个order by把结果给升序了,导致出来的不是正确数据,关键在于order by比select 数据列先执行。

 

 

考点4 数据库中重复的数据

 

这个题目就是超级经典的面试题了,而且解法比较多,这里我做个总结。

 

创建表

 

4-1 查询姓名相同所有重复数据(单字段重复)

 

常见的思路:

1:通过子查询去查询每个姓名的分组数据总和,然后筛选大于1的分组。

2:使用exists查询是否存在相同姓名不同ID的数据。

 

--判断重复数据条数
select * from Student as S1 where
(
select count(id) from Student as S2
where S2.[name]=S1.[name] group by S2.[name]
)>1

--判断ID
select S1.* from Student as S1 where exists
(
select S2.id from Student as S2
where S2.[name]=S1.[name] and S2.id<>S1.id
)
复制代码

 

这2中方法属于比较常见的,如果园友有其它更好的方法请告知。

 

 

4-2 查询重复数据中最早创建的数据(单字段重复)

 

分组已经筛选大于1的分组,然后select分组中最小的id

 

select * from Student where id in
(
select min(id) from Student
group by [name] having count(id)>1
)
复制代码

 

4-3 查询重复数据中非最早创建的数据(单字段重复)

 

去掉重复数据中最早创建的数据,只查询出后面创建的重复数据

1:使用row_number函数,根据重复字段name分区生成行号,去大于1的行号。

2:求出重复name最小的id,非最早创建的重复数据大于这个最小的id.

 

select * from Student where id in
(
select id from
(
select id,row_number() over(partition by [name] order by id)
as num from Student
) as t where num>1
)

select S1.* from Student as S1 where S1.id>
(
select min(S2.id) from Student as S2
where S2.[name]=S1.[name]
)
复制代码

 

4-3 查询多字段重复的数据(name,age,sex)

 

这个和查询单字段重复数据思想都是一样,只是在判断重复字段的地方要写上需要判断的所有字段,如果有更好的方法请联系我谢谢。

 

--判断重复数据条数
select * from Student as S1 where
(
select count(id) from Student as S2
where S2.[name]=S1.[name]
and S2.age=S1.age
and S2.sex=S1.sex
group by S2.[name]
)>1

--判断ID
select S1.* from Student as S1 where exists
(
select S2.id from Student as S2
where S2.[name]=S1.[name]
and S2.age=S1.age
and S2.sex=S1.sex
and S2.id<>S1.id
)
复制代码

 

4-5 删除重复数据

 

要求:删除全部重复数据

 

--判断重复数据条数
delete from Student where id in
(
select id from Student as S1 where
(
select count(id) from Student as S2
where S2.[name]=S1.[name] group by S2.[name]
)>1
)
--判断ID
delete from Student where id in
(
select S1.id from Student as S1 where exists
(
select S2.id from Student as S2
where S2.[name]=S1.[name] and S2.id<>S1.id
)
)
复制代码

 

对于要求保留一条重复数据的题目,可以使用重复数据中最早创建的数据的SQL,获取到id再删除,多字段重复都是相同思路。

 

作者:海不是蓝

博客:http://www.cnblogs.com/hailan2012/

邮箱:hailan2012@sina.com

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

posted @ 2012-03-27 00:00  不服输青年  阅读(374)  评论(0编辑  收藏  举报