SQL点滴26—常见T-SQL面试解析

系统 1460 0
原文: SQL点滴26—常见T-SQL面试解析

 

它山之石可以攻玉,这一篇是读别人的博客后写下的,不是原原本本的转载,加入了自己的分析过程和演练。sql语句可以解决很多的复杂业务,避免过多的项目代码,下面几个语句很值得玩味。

1. 已经知道原表
year salary
2000 1000
2001 2000
2002 3000
2003 4000
怎么查询的到下面的结果,就是累积工资
year salary
2000 1000
2001 3000
2002 6000
2003 10000

思路:这个需要两个表交叉查询得到当前年的所有过往年,然后再对过往年进行聚合。代码如下:

 

      
        create
      
      
        table
      
       #salary(years 
      
        int
      
       ,salary 
      
        int
      
       )
      
insert into #salary values
( 2000 , 1000 ),
( 2001 , 2000 ),
( 2002 , 3000 ),
( 2003 , 4000 )

select b.years, SUM (a.salary)
from #salary a,#salary b
where a.years <= b.years
group by b.years
order by b.years

 

还有一种方法是使用子查询,第一列是年,第二列是所有小于等于第一列这年的工资总和,也比较直接,代码如下:

 

 

      
        select
      
      
s1.years as years,
( select sum (s2.salary) from #salary s2 where s2.years <= s1.years) as salary
from #salary s1

 

 

2. 现在我们假设只有一个 table ,名为 pages ,有四个字段, id, url,title,body 。里面储存了很多网页,网页的 url 地址, title 和网页的内容,然后你用一个 sql 查询将 url 匹配的排在最前, title 匹配的其次, body 匹配最后,没有任何字段匹配的,不返回。

 

思路:做过模糊搜索对这个应该很熟悉的,可以使用 union all 依次向一个临时表中添加记录。这里使用 order by charindex 来是实现,代码如下:

 

      
        create
      
      
        table
      
       #page(id 
      
        int
      
      , url 
      
        varchar
      
      (
      
        100
      
      ),title 
      
        varchar
      
      (
      
        100
      
      ), body 
      
        varchar
      
      (
      
        100
      
      ))
      
insert into #page values
( 1 , null , ' abcde ' , ' abcde ' ),
( 2 , null , ' abcde ' , null ),
( 3 , ' abcde ' , ' e ' , null )

select *
from #page
where url like ' %e% ' or title like ' %e% ' or body like ' %e% '
order by
case when ( charindex ( ' e ' , url) > 0 ) then 1 else 0 end desc ,
case when ( charindex ( ' e ' , title) > 0 ) then 1 else 0 end desc ,
case when ( charindex ( ' e ' , body) > 0 ) then 1 else 0 end desc

只要出现一次就会排在前面,这种情况如果两行都出现就会比较下一个字段,以此类推。

还有一种实现,类似于记分牌的思想,如下:

      
        select
      
       a.
      
        [
      
      
        id
      
      
        ]
      
      ,
      
        sum
      
      (a.mark) 
      
        as
      
       summark  
      
        from
      
      
(
select #page. * , 10 as mark from #page where #page. [ url ] like ' %b% '
union
select #page. * , 5 as mark from #page where #page. [ title ] like ' %b% '
union
select #page. * , 1 as mark from #page where #page. [ body ] like ' %b% '
) as a group by id order by summark desc

 

3. 表内容:
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

思路:首先要有 group by 时间,然后是使用 sum 统计胜负的个数。代码如下:

 

      
        create
      
      
        table
      
       #scores(dates 
      
        varchar
      
      (
      
        10
      
      ),score 
      
        varchar
      
      (
      
        2
      
      ))
      
insert into #scores values
( ' 2005-05-09 ' , ' ' ),
( ' 2005-05-09 ' , ' ' ),
( ' 2005-05-09 ' , ' ' ),
( ' 2005-05-09 ' , ' ' ),
( ' 2005-05-10 ' , ' ' ),
( ' 2005-05-10 ' , ' ' ),
( ' 2005-05-10 ' , ' ' )

select a.dates as [ 比赛时间 ] ,
SUM ( case a.score when ' ' then 1 else 0 end ) as [ ] ,
SUM ( case a.score when ' ' then 1 else 0 end ) as [ ]
from #scores a
group by a.dates

 

还有一种方法是使用子查询,先用两个子查询得到这些日期中的胜负常数,然后连接查询,代码如下:

 

 

      
        select
      
      
t1.dates as [ 比赛时间 ] ,
t1.score as [ ] ,
t2.score as [ ]
from
( select a.dates as dates, COUNT ( 1 ) as score from #scores a where a.score = ' ' group by a.dates) t1 inner join
( select a.dates as dates, COUNT ( 1 ) as score from #scores a where a.score = ' ' group by a.dates) t2 on t1.dates = t2.dates

 

 

 

4. 表中有 A B C 三列 , SQL 语句实现:当 A 列大于 B 列时选择 A 列否则选择 B 列,当 B 列大于 C 列时选择 B 列否则选择 C

 

思路:这个字面意思很简单了,就是二者选其一,使用 case 就可以实现,代码如下:

 

      
        create
      
      
        table
      
       #table3(A 
      
        int
      
      , B 
      
        int
      
       ,C 
      
        int
      
      )
      
insert into #table3 values
( 2 , 1 , 3 ),
( 4 , 2 , 5 )

select
case when A > B then A else B end as AB,
case when B > C then B else C end as BC
from #table3

 

5. 请用一个 sql 语句得出结果

table1,table2 中取出如 table3 所列格式数据,注意提供的数据及结果不准确,只是作为一个格式向大家请教。

table1

月份           部门 业绩

一月份       01       10

一月份       02       10

一月份       03       5

二月份       02       8

二月份       04       9

三月份       03       8

 

table2

部门       部门名称

01       国内业务一部

02       国内业务二部

03       国内业务三部

04       国际业务部

 

table3 result

部门 部门名称   一月份       二月份       三月份

  01   国内业务一部     10         null       null

  02    国内业务二部    10          8         null

    03    国内业务三部    null        5         8

  04    国际业务部    null       null       9

思路:又是行列转换,不过这个稍微复杂一点代码如下:

 

      
        create
      
      
        table
      
       #table4(
      
        [
      
      
        月份
      
      
        ]
      
      
        varchar
      
      (
      
        10
      
      ),
      
        [
      
      
        部门
      
      
        ]
      
      
        varchar
      
      (
      
        10
      
      ),
      
        [
      
      
        业绩
      
      
        ]
      
      
        int
      
      )
      
insert into #table4 values
( ' 一月份 ' , ' 01 ' , ' 10 ' ),
( ' 一月份 ' , ' 02 ' , ' 10 ' ),
( ' 一月份 ' , ' 03 ' , ' 5 ' ),
( ' 二月份 ' , ' 02 ' , ' 8 ' ),
( ' 二月份 ' , ' 04 ' , ' 9 ' ),
( ' 三月份 ' , ' 03 ' , ' 8 ' )

create table #table5( [ 部门 ] varchar ( 10 ), [ 部门名称 ] varchar ( 50 ))
insert into #table5 values
( ' 01 ' , ' 国内业务一部 ' ),
( ' 02 ' , ' 国内业务二部 ' ),
( ' 03 ' , ' 国内业务三部 ' ),
( ' 04 ' , ' 国际业务部 ' )


select [ 部门 ] , [ 部门名称 ] , [ 一月份 ] , [ 二月份 ] , [ 三月份 ]
from ( select a. [ 月份 ] ,a. [ 部门 ] as [ 部门 ] ,b. [ 部门名称 ] ,a. [ 业绩 ] from #table4 a join #table5 b on a. [ 部门 ] = b. [ 部门 ] ) sod
pivot( min (sod. [ 业绩 ] ) for sod. [ 月份 ] in ( [ 一月份 ] , [ 二月份 ] , [ 三月份 ] )) pvt
order by [ 部门 ]

注意,这里每个月份每个部门只有一行数据,所以 pivot 运算的时候可以使用 min 函数,使用 max min 都可以。如果这里有多行数据,那么一般会让计算合计,只能用 sum

还有一种方法是使用子查询,这个代码要多一点,如下:

 

 

      
        select
      
       a.
      
        [
      
      
        部门
      
      
        ]
      
       ,b.
      
        [
      
      
        部门名称
      
      
        ]
      
      ,
      
SUM ( case when a.月份 = ' 一月份 ' then a. [ 业绩 ] else 0 end ) as [ 一月份 ] ,
SUM ( case when a.月份 = ' 二月份 ' then a. [ 业绩 ] else 0 end ) as [ 二月份 ] ,
SUM ( case when a.月份 = ' 三月份 ' then a. [ 业绩 ] else 0 end ) as [ 三月份 ]
from #table4 a inner join #table5 b on a. [ 部门 ] = b. [ 部门 ] group by a. [ 部门 ] ,b. [ 部门名称 ]


6. 表结构以及数据如下:

 

CREATE TABLE #table6

 

(ID int, 日期 varchar(11), 单据 char(3))

 

INSERT INTO (ID , 日期 , 单据 ) VALUES ( 1 , '2004-08-02' , '001' );

 

INSERT INTO (ID , 日期 , 单据 ) VALUES ( 2 , '2004-09-02' , '001' );

 

INSERT INTO (ID , 日期 , 单据 ) VALUES ( 3 , '2004-10-02' , '002' );

 

INSERT INTO (ID , 日期 , 单据 ) VALUES ( 4 , '2004-09-02' , '002' );

 

要求 : 设计一个查询,返回结果如下:

 

ID 日期        单据

 

1 2004-08-02 001

 

4 2004-09-02 002

 

思路:这个是要找到日期比较小的那一条单据,这个有多种方法实现。第一种方法是相关子查询,如下:

 

      
        create
      
      
        table
      
       #table6 
      
(id int , 日期varchar( 11 ), 单据char( 3 ))
insert into #table6 (id , 日期, 单据) values ( 1 , ' 2004-08-02 ' , ' 001 ' );
insert into #table6 (id , 日期, 单据) values ( 2 , ' 2004-09-02 ' , ' 001 ' );
insert into #table6 (id , 日期, 单据) values ( 3 , ' 2004-10-02 ' , ' 002 ' );
insert into #table6 (id , 日期, 单据) values ( 4 , ' 2004-09-02 ' , ' 002 ' );

select * from #table6 a
where a. [ 日期 ] = ( select MIN (b. [ 日期 ] ) from #table6 b where b. [ 单据 ] = a. [ 单据 ] )

还可以使用 join 连接,如下:

      
        select
      
       a.
      
        *
      
      
from #table6 a join
( select b. [ 单据 ] , MIN (b. [ 日期 ] ) as [ 日期 ] from #table6 b group by b. [ 单据 ] ) c
on a. [ 日期 ] = c. [ 日期 ] and a. [ 单据 ] = c. [ 单据 ]

注意最后 on 条件必须是 a.[ 日期 ] = c.[ 日期 ] and a.[ 单据 ] = c.[ 单据 ] ,因为 c 表只是找出来两组符合条件的数据,如果只是 a.[ 日期 ] = c.[ 日期 ] 的话会找出多条不符合要求的数据。

还可以不 使用 join 连接,如下:

      
        select
      
       a.
      
        *
      
      
from #table6 a ,
( select b. [ 单据 ] , MIN (b. [ 日期 ] ) as [ 日期 ] from #table6 b group by b. [ 单据 ] ) c
where a. [ 日期 ] = c. [ 日期 ] and a. [ 单据 ] = c. [ 单据 ]

还可以使用谓词 exist ,如下:

      
        select
      
      
        *
      
      
        from
      
       #table6 a
      
where not exists
( select 1 from #table6 where [ 单据 ] = a. [ 单据 ] and a. [ 日期 ] > [ 日期 ] )

注意not exists 查询筛选得到时间最小的那条记录,注意这里不能使用 exists exists 会得到多条。可以理解为 a 中的日期不会大于子查询中所有日期,就是那个最小的日期。还有去掉[单据]=a.[单据],也会得到更多的数据,这个和普通的情况刚好相反。因为加上这个条件整个子查询会得到更多的数据,否则只保留a.[日期]>[日期]只会得到一条数据。

 

 

7. 已知下面的表

 

id   strvalue type

 

1     how       1

 

2     are       1

 

3     you       1

 

4     fine      2

 

5     thank     2

 

6     you       2

 

要求用 sql 把它们搜索出来成为这样的

 

#how are you#fine thank you#

 

思路:这个和上一篇中的最后一题很相似,也是连接有相同字段的字符,上回使用游标实现的,这次用 for xml 来实现,代码如下:

      
        create
      
      
        table
      
       #table7(id 
      
        int
      
      ,strvalue 
      
        varchar
      
      (
      
        20
      
      ),typ 
      
        int
      
      )
      
insert into #table7 values
( 1 , ' how ' , 1 ),
( 2 , ' are ' , 1 ),
( 3 , ' you ' , 1 ),
( 4 , ' fine ' , 2 ),
( 5 , ' thank ' , 2 ),
( 6 , ' you ' , 2 )
select * from #table7

select
( select ' # ' + replace ( replace (( select strvalue from #table7 t where typ = 1 for xml auto), ' <t strvalue=" ' , ' ' ), ' "/> ' , ' ' ) + ' # ' )
+
( select replace ( replace (( select strvalue from #table7 t where typ = 2 for xml auto), ' <t strvalue=" ' , ' ' ), ' "/> ' , ' ' ) + ' # ' )

或者这样

      
        select
      
      
        '
      
      
        #
      
      
        '
      
      
        +
      
      
ltrim (( select ' ' + a.strvalue from #table7 a where a.typ = 1 for xml path( '' ))) + ' # ' +
ltrim (( select ' ' + a.strvalue from #table7 a where a.typ = 2 for xml path( '' ))) + ' # '

或者这样,用变量来处理

 

      
        declare
      
      
        @value
      
      
        varchar
      
      (
      
        1000
      
      )
      
        =
      
      
        '
      
      
        #
      
      
        '
      
      
select @value = '' + @value + a.strvalue + ' ' from #table7 a where a.typ = 1
select @value = @value + ' # '
select @value = @value + a.strvalue + ' ' from #table7 a where a.typ = 2
select @value = @value + ' # '
print @value

 

for xml 是好东西啊,是解决这类字符连接问题的利刃

 






 

 

SQL点滴26—常见T-SQL面试解析


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论