第1课 SQL入门之了解SQL
第2课 SQL入门之检索数据
第3课 SQL入门之排序检索数据
第4课 SQL入门之过滤数据
第5课 SQL入门之高级数据过滤
第6课 SQL入门之用通配符进行过滤
第7课 SQL入门之创建计算字段
第8课 SQL入门之使用数据处理函数
第9课 SQL入门之汇总数据
第10课 SQL入门之分组数据
第11课 SQL入门之使用子查询
第12课 SQL入门之联结表
第13课 SQL入门之创建高级联结
第14课 SQL入门之组合查询
第15课 SQL入门之插入数据
第16课 SQL入门之更新和删除数据
第17课 SQL入门之创建和操纵表
第18课 SQL入门之使用视图
第19课 SQL入门之使用存储过程
第20课 SQL入门之管理事务处理
第21课 SQL入门之使用游标
第22课 SQL入门之高级SQL特性
附录A SQL入门之SQL样例表脚本
附录B SQL入门之SQL流行的应用程序
附录C SQL入门之SQL语句的语法
附录D SQL入门之SQL数据类型
附录E SQL入门之SQL保留字
与大多数其他计算机语言一样,SQL也可以用函数来处理数据。函数一般是在数据上执行的,为数据的转换和处理提供了方便。
前一课中用来去掉字符串尾的空格的RTRIM()就是一个函数。
函数带来的问题
在学习这一课并进行实践之前,你应该了解使用SQL函数所存在的问题。
与几乎所有DBMS都等同地支持SQL语句(如SELECT)不同,每一个DBMS都有特定的函数。事实上,只有少数几个函数被所有主要的DBMS等同地支持。虽然所有类型的函数一般都可以在每个DBMS中使用,但各个函数的名称和语法可能极其不同。为了说明可能存在的问题,表8-1列出了3个常用的函数及其在各个DBMS中的语法:
表8-1 DBMS函数的差异
函 数 | 语 法 |
---|---|
提取字符串的组成部分 | Access使用MID(); DB2、Oracle、PostgreSQL和SQLite使用SUBSTR(); MySQL和SQL Server使用SUBSTRING() |
数据类型转换 | Access和Oracle使用多个函数,每种类型的转换有一个函数; DB2和PostgreSQL使用CAST(); MariaDB、MySQL和SQL Server使用CONVERT() |
取当前日期 | Access使用NOW(); DB2和PostgreSQL使用CURRENT_DATE; MariaDB和MySQL使用CURDATE(); Oracle使用SYSDATE; SQL Server使用GETDATE(); SQLite使用DATE() |
可以看到,与SQL语句不一样,SQL函数不是可移植的。这表示为特定SQL实现编写的代码在其他实现中可能不正常。
可移植(portable)
所编写的代码可以在多个系统上运行。
为了代码的可移植,许多SQL程序员不赞成使用特定于实现的功能。虽然这样做很有好处,但有的时候并不利于应用程序的性能。如果不使用这些函数,编写某些应用程序代码会很艰难。必须利用其他方法来实现DBMS可以非常有效完成的工作。
提示:是否应该使用函数?
现在,你面临是否应该使用函数的选择。决定权在你,使用或是不使用也没有对错之分。如果你决定使用函数,应该保证做好代码注释,以便以后你(或其他人)能确切地知道所编写的SQL代码的含义。
大多数SQL实现支持以下类型的函数。
在上一课,我们已经看过一个文本处理函数的例子,其中使用RTRIM()函数来去除列值右边的空格。下面是另一个例子,这次使用的是UPPER()函数:
输入▼
SELECT vend_name, UPPER(vend_name)
AS vend_name_upcase
FROM Vendors
ORDER BY vend_name;
输出▼
vend_name | vend_name_upcase |
---|---|
Bear Emporium | BEAR EMPORIUM |
Bears R Us | BEARS R US |
Doll House Inc. | DOLL HOUSE INC. |
Fun and Games | FUN AND GAMES |
Furball Inc. | FURBALL INC. |
Jouets et ours | JOUETS ET OURS |
分析▼
可以看到,UPPER()将文本转换为大写,因此本例子中每个供应商都列出两次,第一次为Vendors表中存储的值,第二次作为列vend_name_upcase转换为大写。
表8-2列出了一些常用的文本处理函数。
表8-2 常用的文本处理函数
函 数 | 说 明 |
---|---|
LEFT()(或使用子字符串函数) | 返回字符串左边的字符 |
LENGTH()(也使用DATALENGTH()或LEN()) | 返回字符串的长度 |
LOWER()(Access使用LCASE()) | 将字符串转换为小写 |
LTRIM() | 去掉字符串左边的空格 |
RIGHT()(或使用子字符串函数) | 返回字符串右边的字符 |
RTRIM() | 去掉字符串右边的空格 |
SOUNDEX() | 返回字符串的SOUNDEX值 |
UPPER()(Access使用UCASE()) | 将字符串转换为大写 |
表8-2中的SOUNDEX需要做进一步的解释。SOUNDEX是一个将任何文本串转换为描述其语音表示的字母数字模式的算法。SOUNDEX考虑了类似的发音字符和音节,使得能对字符串进行发音比较而不是字母比较。虽然SOUNDEX不是SQL概念,但多数DBMS都提供对SOUNDEX的支持。
说明:SOUNDEX支持 Microsoft Access和PostgreSQL不支持SOUNDEX(),因此以下的例子不适用于这些DBMS。
另外,如果在创建SQLite时使用了SQLITE_SOUNDEX编译时选项,那么SOUNDEX()在SQLite中就可用。因为SQLITE_SOUNDEX不是默认的编译时选项,所以多数SQLite实现不支持SOUNDEX()。
下面给出一个使用SOUNDEX()函数的例子。Customers表中有一个顾客Kids Place,其联系名为Michelle Green。但如果这是错误的输入,此联系名实际上应该是Michael Green,该怎么办呢?显然,按正确的联系名搜索不会返回数据,如下所示:
输入▼
SELECT cust_name, cust_contact
FROM Customers
WHERE cust_contact = 'Michael Green';
输出▼
cust_name | cust_contact |
---|---|
现在试一下使用SOUNDEX()函数进行搜索,它匹配所有发音类似于Michael Green的联系名:
输入▼
SELECT cust_name, cust_contact
FROM Customers
WHERE SOUNDEX(cust_contact) = SOUNDEX('Michael Green');
输出▼
cust_name | cust_contact |
---|---|
Kids Place | Michelle Green |
分析▼
在这个例子中,WHERE子句使用SOUNDEX()函数把cust_contact列值和搜索字符串转换为它们的SOUNDEX值。因为Michael Green和Michelle Green发音相似,所以它们的SOUNDEX值匹配,因此WHERE子句正确地过滤出了所需的数据。
日期和时间采用相应的数据类型存储在表中,每种DBMS都有自己的特殊形式。日期和时间值以特殊的格式存储,以便能快速和有效地排序或过滤,并且节省物理存储空间。
应用程序一般不使用日期和时间的存储格式,因此日期和时间函数总是用来读取、统计和处理这些值。由于这个原因,日期和时间函数在SQL中具有重要的作用。遗憾的是,它们很不一致,可移植性最差。
我们举个简单的例子,来说明日期处理函数的用法。Orders表中包含的订单都带有订单日期。为在SQL Server中检索2012年的所有订单,可如下进行:
输入▼
SELECT order_num
FROM Orders
WHERE DATEPART(yy, order_date) = 2012;
输出▼
order_num |
---|
20005 |
20006 |
20007 |
20008 |
20009 |
在Access中使用如下版本:
输入▼
SELECT order_num
FROM Orders
WHERE DATEPART('yyyy', order_date) = 2012;
分析▼
这个例子(SQL Server和Sybase版本以及Access版本)使用了DATEPART()函数,顾名思义,此函数返回日期的某一部分。DATEPART()函数有两个参数,它们分别是返回的成分和从中返回成分的日期。在此例子中,DATEPART()只从order_date列中返回年份。通过与2012比较,WHERE子句只过滤出此年份的订单。
下面是使用名为DATE_PART()的类似函数的PostgreSQL版本:
输入▼
SELECT order_num FROM Orders WHERE DATE_PART(‘year’, order_date) = 2012;
Oracle没有DATEPART()函数,不过有几个可用来完成相同检索的日期处理函数。例如:
输入▼
SELECT order_num
FROM Orders
WHERE to_number(to_char(order_date, 'YYYY')) = 2012;
分析▼
在这个例子中,to_char()函数用来提取日期的成分,to_number()用来将提取出的成分转换为数值,以便能与2012进行比较。
完成相同工作的另一方法是使用BETWEEN操作符:
输入▼
SELECT order_num
FROM Orders
WHERE order_date BETWEEN to_date('01-01-2012')
AND to_date('12-31-2012');
分析▼
在此例子中,Oracle的to_date()函数用来将两个字符串转换为日期。一个包含2012年1月1日,另一个包含2012年12月31日。BETWEEN操作符用来找出两个日期之间的所有订单。值得注意的是,相同的代码在SQL Server中不起作用,因为它不支持to_date()函数。但是,如果用DATEPART()替换to_date(),当然可以使用这种类型的语句。
MySQL和MariaDB具有各种日期处理函数,但没有DATEPART()。MySQL和MariaDB用户可使用名为YEAR()的函数从日期中提取年份:
输入▼
SELECT order_num
FROM Orders
WHERE YEAR(order_date) = 2012;
在SQLite中有个小技巧:
输入▼
SELECT order_num
FROM Orders
WHERE strftime('%Y', order_date) = 2012;
这里给出的例子提取和使用日期的成分(年)。按月份过滤,可以进行相同的处理,指定AND操作符以及年和月份的比较。
DBMS提供的功能远不止简单的日期成分提取。大多数DBMS具有比较日期、执行基于日期的运算、选择日期格式等的函数。但是,可以看到,不同DBMS的日期-时间处理函数可能不同。关于具体DBMS支持的日期-时间处理函数,请参阅相应的文档。
数值处理函数仅处理数值数据。这些函数一般主要用于代数、三角或几何运算,因此不像字符串或日期-时间处理函数使用那么频繁。
具有讽刺意味的是,在主要DBMS的函数中,数值函数是最一致、最统一的函数。表8-3列出一些常用的数值处理函数。
函 数 | 说 明 |
---|---|
ABS() | 返回一个数的绝对值 |
COS() | 返回一个角度的余弦 |
EXP() | 返回一个数的指数值 |
PI() | 返回圆周率 |
SIN() | 返回一个角度的正弦 |
SQRT() | 返回一个数的平方根 |
TAN() | 返回一个角度的正切 |
关于具体DBMS所支持的算术处理函数,请参阅相应的文档。
上一篇:第7课 SQL入门之创建计算字段
下一篇:第9课 SQL入门之汇总数据