SQL点滴21—几个有点偏的语句

系统 2074 0
原文: SQL点滴21—几个有点偏的语句

SQL 语句是一种集合操作,就是批量操作,它的速度要比其他的语言快,所以在设计的时候很多的逻辑都会放在 sql 语句或者存储过程中来实现,这个是一种设计思想。但是今天我们来讨论另外一个话题。 Sql 页提供了丰富的函数供我们使用,还有很多操作有意想不到的结果,今天这个随笔来看看一些不常见到的 sql 语句。这些语句不像普通的增删查那样平白,它的奇妙之处有时候让人另眼相看。

1.  假设我想把 Person.Contact 表中所有人的名字用逗号连接起来,串成一个字符串,可能会想到使用游标把 FirstName 查出来然后逐行赋值给一个字符串变量,可是使用游标的代价是很大的。看看下面的代码:

      
        declare
      
      
      
      
        @names
      
      
      
      
        varchar
      
      
        (
      
      
        1000
      
      
        )
      
      
        =
      
      
        ''
      
      
        —注意赋值为空字符串是必须的
        
select @names = isnull ( @names , '' ) + FirstName + ' , ' from Person.Contact
print @names

 

查询得到的结果是(用的是 AdventureWorks 数据库中的 Contact 表): Gustavo,Catherine,Kim,Humberto,Pilar,Frances,Margaret,Carla,Jay,Ronald,Samuel,James,Robert,Fran?ois,Kim,Lili,Amy,Anna,Milton,Paul,Gregory,J. Phillip,Michelle,Sean,Phyllis,Marvin,Michael,Cecil,Oscar,Sandra,Selena,Emilio,Maxwell,Mae,Ramona,Sabria,Hannah,Kyley,Tom,Thomas,John,Chris,

使用其他的语句是不能达到这个效果的,不过我没有深入考虑过,但是这个是很简单的语句。

还有一个地方和这个类似,就是在行列转换的时候拼接动态 sql 语句,首先使用下面的语句创建一个临时表:

      
        create
      
      
      
      
        table
      
      
         #DepartCost
        
(
id
int ,
Department
varchar ( 20 ),
Material
varchar ( 20 ),
Number int
)
insert into #DepartCost values
(
1 , ' 厂房1 ' , ' 材料1 ' , 1 ),
(
1 , ' 厂房2 ' , ' 材料2 ' , 2 ),
(
1 , ' 厂房1 ' , ' 材料3 ' , 1 ),
(
1 , ' 厂房3 ' , ' 材料3 ' , 1 ),
(
1 , ' 厂房2 ' , ' 材料3 ' , 1 ),
(
1 , ' 厂房3 ' , ' 材料1 ' , 1 ),
(
1 , ' 厂房1 ' , ' 材料1 ' , 2 ),
(
1 , ' 厂房1 ' , ' 材料2 ' , 1 ),
(
1 , ' 厂房1 ' , ' 材料3 ' , 1 )

表中的数据如下:

SQL点滴21—几个有点偏的语句

图1

我们看到每个厂房分别使用的材料数量,还是一个老问题,如果我们想知道针对每种材料,每个厂房耗费的材料数量是多少该怎么写呢。有一种笨的方法,如下:

      
        select
      
      
         Department,
        
sum ( case Material when ' 材料1 ' then Number else 0 end ) as [ 材料1 ] ,
sum ( case Material when ' 材料2 ' then Number else 0 end ) as [ 材料2 ] ,
sum ( case Material when ' 材料3 ' then Number else 0 end ) as [ 材料3 ]
from #DepartCost
group by Department

查询结果如下:

SQL点滴21—几个有点偏的语句

图2

说这种方法笨是因为需要事先知道材料的类别,如果有很多种材料这个语句就会很长了,下面我们使用动态语句来实现这个功能:

      
        declare
      
      
      
      
        @sql
      
      
      
      
        varchar
      
      
        (
      
      
        1000
      
      
        )
        
set @sql = ' select Department '
select @sql = @sql + ' , sum(case Material when ''' + Material + ''' then Number else 0 end) as [ ' + Material + ' ] ' from
(
select distinct Material from #DepartCost) as a
select @sql = @sql + ' from #DepartCost group by Department '
exec ( @sql )

我们来看看 @sql 字符串变量到底长得什么样子,使用 print @sql 将它打印出来:

select Department , sum(case Material when ' 材料 ' then Number else 0 end) as [ 材料 ], sum(case Material when ' 材料 ' then Number else 0 end) as [ 材料 ], sum(case Material when ' 材料 ' then Number else 0 end) as [ 材料 ] from #DepartCost group by Department

 

这个语句和上面那个是一样的,当然 exec(@sql) 得到的结果也是一样的了。这里我不知道这种特性有个什么说法,不像子查询,也不是 case 语句。

2. 写一个语句获得当前这个月有多少天

这个涉及到日期和时间,初步的思路是查询得到本月的最后一天,然后用 datepart 获得天数,这是一个很直接的方法。来看下面的语句:

      
        select
      
      
        
datepart (
dd,
-- datepart的参数取本月最后一天的天数,即为本月的天数
dateadd (dd, -- 取下个月的第一天的前一天,就是本月最后一天
- 1 ,
dateadd (mm, -- 取下一个月的第一天
1 ,
cast ( cast ( year ( getdate ()) as varchar ) + ' - ' + -- 取当前的年
cast ( month ( getdate ()) as varchar ) + ' -01 ' -- 取这个月的第一天
as datetime ))) -- 转换成时间
)

这个语句没有什么悬念,仅仅是时间函数的使用,只要知道这个思路就很容易写出来。

3. 假设我们有一张销售表,现在要查出销售单价,但是我们想不适用具体的价钱来显示,而是显示为一个范围,比如价钱是 1-100 元要显示“ 1 to 100 ”, 100-200 要显示“ 100 to 200 ”,等等。来看代码:

      
        select
      
      
         so.UnitPrice, NewUnitPrice 
      
      
        =
      
      
        
case when so.UnitPrice is null then ' unknown ' -- NewPrice一点类似于C#里面的var变量,事先不定义类型,从赋值结果里面确认它的类型
when so.UnitPrice between 100 and 200 then ' 100 to 200 '
when so.UnitPrice between 201 and 300 then ' 200 to 300 '
when so.UnitPrice between 301 and 400 then ' 300 to 400 '
else cast (so.UnitPrice as varchar ( 10 )) -- 这里一定要转换成字符串
end
from Sales.SalesOrderDetail so order by UnitPrice

要注意的是最后剩下一些不做归类转换的必须将类型转换为 varchar ,否则会有语法错误。 结果如下:

SQL点滴21—几个有点偏的语句

图3

 

4. 假设有一张联系人姓名表,现在想查出这个表中姓相同的联系人的数目,猛一看有点懵,其实很简单,来看代码:

      
        select
      
      
         c.LastName,num_LastName
      
      
        =
      
      
        COUNT
      
      
        (
      
      
        1
      
      
        ) 
      
      
        from
      
      
         Person.Contact c 
      
      
        group
      
      
      
      
        by
      
      
         c.LastName
      
    

SQL点滴21—几个有点偏的语句

图4

注意要统计那个字段就要对那个字段进行聚合操作,如图我们可以看到有 77 个姓 Davis 的, 71 个姓 Lin 的, 90 个姓 Waston 的等等。

5.查找数据库中所有表的行数

      
        select
      
       ROW_NUMBER() 
      
        over
      
      (
      
        order
      
      
        by
      
       TABLE_NAME) 
      
        as
      
       rownumber,TABLE_SCHEMA, TABLE_NAME 
      
        into
      
       #
      
        table
      
      
        from
      
       INFORMATION_SCHEMA.TABLES 
      
        where
      
       TABLE_TYPE
      
        =
      
      
        '
      
      
        BASE TABLE
      
      
        '
      
      
declare @count int
select @count = COUNT ( * ) from # table
declare @index int = 1
declare @tablename nvarchar ( 200 )
declare @sql nvarchar ( 1000 )
while @index < @count
begin
select @tablename = TABLE_SCHEMA + ' . ' + TABLE_NAME from # table where rownumber = @index
select @sql = ' select ''' + @tablename + ''' as tablename, COUNT(*) as rowscount from ' + @tablename
exec ( @sql )
if @index > @count
break
set @index = @index + 1
end
drop table # table

这个方法很一般,求教高手们提供一个更加灵活的方法。

SQL点滴21—几个有点偏的语句


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

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

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

【本文对您有帮助就好】

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

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