SQL语言中的常用函数主要包括6大类:文本函数、数学函数、日期函数、类型转换函数、条件判断函数及聚合统计函数。其中,聚合函数最常见的就是sum、avg、c++ount、max和min,它们在两种数据库中的用法完全相同,且非常简单,此略。
阅读提示:本文节选自笔者编著的《超越Excel》一书,篇幅较长,请耐心阅读。由于这些函数都是日常开发中常用的,如需此文的完整pdf电子版,敬请留言。
文本函数access和SQlserver中的常用文本函数如下表:
上表中的列名也可为表达式。其他函数同。
对于access数据库,除了“+”外,还可使用“&”拼接字符串。两者的区别在于:“+”只能拼接字符型的数据,而“&”还可将其他类型数据自动转为字符串再做拼接。例如:
select 产品id & 数量 & 日期 from 订单
但此种用法仅限于Access数据库,SQLServer必须将其他类型数据转为字符串之后再使用“+”拼接。
数学函数常用数学函数如下表:
使用以上数学函数时需要注意以下两点:
第一,如果是两个整数相除,不同数据源将得到不同的结果。例如以下语句:
Select 5/4
在Access中的返回值为1.25,SQLServer中的返回值则为1。如果要在SQLServer中得到准确的数据,需使用非整类型的数据。例如,将算式改为“5.0/4.0”得到的数据就是1.25。当然,实际应用中只要参与计算的列不是非整类型就无需担心这样的问题。
Access中还专门提供了一个运算符“\”,用于获取相除后的整数(小数直接舍弃,不会四舍五入)。例如,以下语句在Access数据源中的返回值是1:
select 3.4\2
第二,Int和Fix都会直接删除number的小数部分而返回剩下的整数,两者的区别在于对负数的处理上。
例如,对于数值“-8.4”,Int函数的返回值是-9,作用同Floor,也就是向下取整;而Fix的返回值是-8,作用同Ceiling。
由于Int和Fix对于正数的处理效果都是一样的,全部是向下取整。如要实现向上取整的功能,可通过下面的代码变相实现:
select Int(5.678)+1
日期时间函数SQL语言中的日期时间函数非常灵活。不论是Access还是SQLServer,都可以直接通过Year、Month、Day三个函数获取日期中的年份、月度和具体的日期数,Access甚至还能用WeekDay及Hour、Minute、second等函数直接得到日期时间中的星期数和时、分、秒。
除了上述几个最常见的日期时间函数外,其他常用函数如下表:
由上表可以看出,datePart函数非常强大,不仅可以返回年月日和时分秒,还能返回季度、星期等。除了返回小时数的用法略有不同外,其他各种返回值在Access和SQLServer中的用法都是完全一致的。其中,星期数以星期日为一周的开始,从星期日到星期六的返回值分别是1-7。
当在Access中使用DatePart函数时,第一个参数必须使用单引号或双引号;在SQLServer中使用时,则只能用双引号或不用引号。
至于系统日期和时间,Access是通过Date、Time和Now三个函数分别获取的,而SQLServer则全部使用的GetDate函数。GetDate函数默认仅返回系统日期,如要同时返回时间还需convert函数的配合。
DateAdd函数用于返回相加后的日期,它包含3个参数:第1个是指相加日期的类型,可选项包括DatePart函数所能返回的十种类型;第2个参数表示相加的数值,可以是负数;第3个参数为日期列或日期类型表达式。例如:
select 日期,DateAdd("m",3,日期) as 向后日期,DateAdd("m",-3,日期) as 向前日期 from 订单
由于这里相加的类型是“m”,当指定3时,将在当前日期的基础上加3个月;当指定-3时,则减3个月。查询结果如下图:
Datediff函数用于返回两个日期时间的间隔数,它同样包括3个参数:第1个参数的意义与DateAdd中的同位置参数相同,第2个参数表示起始日期,第3个参数表示结束日期。例如,以下语句的返回值为129,表明两个日期之间相差129天:
select DateDiff("d",'2012-3-5','2012-7-12')
这里有个特殊情况:当把第1个参数改为“w”时,表示计算两个日期之间相差多少周。经测试,同样的语句在Access中得到的值为18,这是正确的;但SQLServer中的值却为129。如果改为“ww”,则两种数据库的返回值都是18。因此,当需要在SQLServer中对日期进行以“周”为单位的相加或间隔运算时,一定要使用“ww”而不是“w”!
再如,要加载最近两个月的订单数据,可使用如下语句:
select * from 订单 where datediff("m", 日期, date()) < 2 'Access的写法
select * from 订单 where datediff("m", 日期, getdate()) < 2 'SQLServer的写法
数据类型转换函数SQL语言中的数据类型转换函数,在Access和SQLServer间并不通用。常用转换函数如下表:
需要注意的是,上述转换函数并不是万能的。比如,非数值形式的字符型数据就不能转换成整数型。使用时请注意。
Access中的数据转换函数使用起来非常简单。例如,要将“产品ID”列和“数量”列拼接到一起,就应该将“数量”列转换为字符型。语句如下:
select 产品id + cstr(数量) from 订单
如果仅仅是转为字符型,也可以不用转换函数,直接改用“&”拼接也是可以的。
SQLServer中虽然只有两个转换函数,但它们的通用性极强,都可将指定数据转换为另外一种类型。而且,Convert函数还常用于不同格式的日期时间转换,功能非常强大。
❶ Cast函数
该函数的语法格式为:
Cast(expression AS data_type[(length)])
其中,expression是任何有效的表达式,包括列名;data_type表示要转换的数据类型;length表示要转换的数据类型长度,此参数可选。例如:
select 产品id + cast(数量 as nvarchar(10)) from 订单
❷ Convert函数
该函数的语法格式为:
Convert(data_type[(length)],expression[,style])
该函数中的前两个参数是必需的,其含义与Cast中的同名参数相同。例如:
select 产品id + convert(nvarchar(10),数量) from 订单
第三个参数style虽然是可选的,但起到的作用非常大,它将决定日期型数据在转换为字符之后的格式。假如,当前的系统时间是2018年4月13日9时20分18秒626毫秒,在没有使用style参数时,代码如下:
select convert(nvarchar,getdate())
执行后的返回值为“04 13 2018 9:20AM”,也就是仅输出当前系统时间以12小时制表示的分钟数。如果希望得到符合中文习惯的完整年月日及时分秒,可将style参数设置为20或120。例如:
select convert(nvarchar,getdate(),120)
这样得到的值就是“2018-04-13 09:20:18”。
Convert函数的常用日期样式如下表:
由上表可以发现,1-8和101-108、10-12和110-112的样式差别仅在于年份上:前面的不带世纪数位,后面的带世纪数位。
事实上,style参数除了可以指定日期转换样式外,还可对单精度小数、双精度小数等指定转换格式。这方面的应用需求极少,此略。
条件判断函数SQL语言中的条件判断函数,Access和SQLServer是完全不同的:Access常用的是iif、switch和choose函数,而SQLServer使用的则是case表达式。
不论是函数还是表达式,它们起到的作用是相同的,且都有返回值。
❶ Access中的判断函数
这里的iif函数用法和表达式中的iif函数完全相同。例如,查询“订单”表中折扣小于0.2的数据记录(包括折扣为空的记录),可以写成这样:
select * from 订单 where iif(折扣 is null,0,折扣) < 0.2
select * from 订单 where iif(isnull(折扣),0,折扣) < 0.2
该函数也常常用于select子句中。例如:
select 产品id, 折扣, iif(iif(折扣 is null,0,折扣)<0.2,'折扣太低','折扣正常') as 折扣说明 from 订单
查询结果如图所示。
还能用于分组或排序子句中。例如,我们先用iif函数编写出如下代码:
iif(iif(折扣 is null,0,折扣)<0.1,'不到1折',iif(iif(折扣 is null,0,折扣)>0.3,'大于3折','1-3折'))
该代码的意思是,如果折扣列的值小于0.1,返回的内容是“不到1折”;如果大于0.3,返回“大于3折”;在0.1和0.3之间的,返回“1-3折”。如果将这个代码同时用于select、group by和order by子句中,可以这样写(为帮助大家更清晰的看出此语句的逻辑,上述代码以expr代替):
select expr as 扣率,sum(数量) as 销量 from 订单 group by expr order by expr desc
执行效果如图所示。
很显然,如果将该逻辑判断再加上0.4、0.5、0.6等各种折扣情况,使用iif函数写起来就太麻烦了。为此,Access又专门增加了Switch函数。
仍以上图中的代码为例,如果改用Switch函数来写,就非常清晰:
switch(折扣 is null or 折扣<0.1,'不到1折',折扣 between 0.1 and 0.3,'1-3折',折扣>0.3,'大于3折')
由此可见,Switch函数中的判断与返回值都是成对出现的:每做一次判断就要给出一个返回值,而且可以根据需要随意增加。
除此之外,与Switch相类似的还有一个Choose函数,该函数是根据一个表达式的计算结果,来决定返回哪个位置的值。请注意,这里的表达式返回值必须是整数,小数的话将直接舍弃小数部分。例如:
select 折扣,choose(iif(折扣 is null,0,折扣)*10,'1折','2折','3折') as 扣率 from 订单
为什么要在上述语句中的判断表达式中乘以10?这是因为,“订单”表中的折扣列数据全部小于1,这样它就在Choose函数中找不到对应位置的值了。为了得到大于1的数据,以便和后面的序列化位置相对应,只能将其乘以10。
上述SQL语句的执行效果如下:
其中,折扣为0.1时,乘以10以后得到的值为1,因此取位置为1的返回值就是“1折”;0.33乘以10以后得到的值是3.3,那就取位置为3的返回值。其它同理。
可是,当折扣为空的时候呢?或者该列的值是负数呢?那就需要在Choose外面再嵌套其它函数。因此,Choose函数仅在对一些序列化的整数进行判断时才会使用,平时使用频率最高的还是IIF、Switch函数。
❷ SQLServer中的判断语句
SQLServer使用Case…End语句来进行条件判断。请注意,这个不是函数,它就是一个条件表达式,且必须以Case开头、以End结束,起到的效果和Access中的函数是相同的。
Case…End语句有两种写法。第一种语法格式为:
Case 表达式
When 比较值1 Then 返回值1
When 比较值2 Then 返回值2
When 比较值3 Then 返回值3
……
Else 默认返回值
End
当表达式等于“比较值1”时,则得到“返回值1”;当表达式等于“比较值2”时,则得到“返回值2”,其余类推。当比较结束,没有符合的比较值时,则得到默认返回值。例如,同样对“订单”表中的折扣列进行判断:
很显然,以上这种写法很难穷尽折扣中的各种情况:除了0.1、0.2、0.3之外,还有各种两位小数的扣率。因此,Case…End语句又提供了另外一种格式的写法:
Case
When 表达式1 Then 返回值1
When 表达式2 Then 返回值2
When 表达式3 Then 返回值3
……
Else 默认返回值
End
当“表达式1”成立时,得到“返回值1”;当“表达式2”成立时,得到“返回值2”,其余类推。
所有表达式都不成立时,则得到默认返回值。如采用此种写法,就可将上述语句修改为:
select 折扣,case when isnull(折扣,0)=0 then '无折扣' when 折扣<=0.1 then '1折以内' when 折扣<=0.2 then '2折以内' when 折扣<=0.3 then '3折以内' else '其它折扣' end as 折扣说明 from 订单
再比如,当在分组统计中使用rollup或cube运算符在统计结果中添加汇总行时,这些行的分组列中的值都是空的。如下图:
现在我们就可以通过Case…End语句来给这些空值加上相应的内容。如下图:
其中,grouping函数仅在分组中使用了rollup或cube时有效,通过它可以判断分组列中的空值究竟是由分组产生的还是原来就有的:当返回值为1时,表示是分组产生的空值;为0时表示数据固有的空值。如果“订单”表中的这两个分组列本来就存在空值的话,那么统计结果中将显示为“unknown”。
假如希望在这个分组统计表中同时得到占比数据,只需在上图所示的代码中再增加一个生成占比数据的表达式即可:
如果想在上表中再同时增加“产品”小计值在总计中的占比数据呢?这个问题就太简单了,请各位自己琢磨一下解决吧。