第7章 数据库查询
第8章 使用操作符对数据进行分类
第9章 汇总查询得到的数据
第10章 数据排序与分组
第11章 调整数据的外观
第12章 日期和时间
本章的重点包括:
什么是数据库查询
如何使用SELECT语句
利用WHERE子句为查询添加条件
使用列别名
从其他用户的表里选择数据
本章将介绍数据库查询,主要是SELECT语句的使用。在数据库创建之后,SQL命令里最常用的语句就是SELECT,它让我们可以查看数据库里保存的数据。
查询是使用SELECT语句对数据库进行探究。我们利用查询,根据需要从数据库里以一种可理解的格式提取数据。举例来说,假设我们有一个雇员表,就可能利用SQL语句返回哪个雇员得到最高的薪水。这种获取有用信息的请求是关系型数据库里典型的查询操作。
SELECT 语句代表了 SQL 里的数据查询语言(DQL),是构成数据库查询的基本语句。它并不是一个单独的语句,也就是说,为了构成一个在句法上正确的查询,需要一个或多个条件子句(元素)。除了必要的子句,还有其他一些可选的子句可以增强SELECT语句的整体功能。SELECT语句绝对是SQL里功能最强大的。FROM子句是一条必要的子句,必须总是与SELECT联合使用。
SELECT语句里有4个关键字(或称为子句)是最有价值的,如下所示:
SELECT
FROM
WHERE
ORDER BY
下面的小节将详细介绍这些关键字。
SELECT语句与FROM子句联合使用,以一种有组织的、可读的方式从数据库提取数据。查询中的SELECT部分用于指定需要表里哪些字段的数据。
简单的SELECT语句的语法如下所示:

在查询里,关键字 SELECT 后面是字段列表,它们是查询输出的组成部分。星号(*)表示输出结果里包含表里的全部字段,其详细使用方式请查看相应实现的文档。选项ALL用于显示一列的全部值,包括重复值。选项DISTINCT禁止在输出结果里包含重复的行。选项ALL是默认的操作方式,这意味着它并不在SELECT语句中明确指定。关键字FROM后面是一个或多个表的名称,用于指定数据的来源。SELECT语句后面的字段列表中使用逗号进行分隔,FROM子句里的表也是如此。
注意:使用逗号来分隔参数
在SQL语句的列表里,使用逗号分隔各个参数。参数是SQL语句或命令里必需的或可选的值。常见的参数列表包括查询中的字段列表、查询中的表列表、插入到表里的数据列表、WHERE子句里的条件。
下面的范例将展示SELECT语句的基本功能。首先,对表PRODUCTS_TBL进行一个简单的查询:

星号表示表里的全部字段,也就是PROD_ID、PROD_DESC和COST。字段在输出结果中显示的次序与其在表里的次序相同。表里一共有 11 条记录,这是由反馈信息“11 rows selected”反映出来的。反馈信息的表示方式在不同实现里有所区别,比如有些查询的反馈信息是“11 rows affected”。星号是书写SQL查询的一种行之有效的便捷方法,但实际操作时, 最好还是明确地指出所要返回的字段名称。
现在从另一个表 CANDY_TBL 里选择数据,这个表与 PRODUCTS_TBL 具有同样的结构。在关键字SELECT之后列出字段名称,从而只显示表里的一个字段:

表CANDY_TBL里有4条记录。下面的语句使用选项ALL,其结果会展示出ALL完全是多余的,它是默认选项,不需要明确指定。

下面的语句使用了DISTINCT选项,从而在显示中去除了重复记录,所以这一次CANDY CORN只显示了一次。

我们还可以用圆括号把选项DISTINCT和ALL与相应的字段包围在一起。在SQL及其他很多语言里,我们都利用圆括号来提高代码的可读性。

FROM子句必须与SLELCT语句联合使用,它是任何查询的必要元素,其作用是告诉数据库从哪些表里获取所需的数据,它可以指定一个或多个表,但必须至少指定一个表。
FORM子句的语法如下所示:

查询里的条件指定了要返回满足什么标准的信息。条件的值是TRUE或FALSE,从而限制查询中获取的数据。WHERE子句用于给查询添加条件,从而去除用户不需要的数据。
WHERE子句里可以有多个条件,它们之间以操作符 AND或 OR 连接,详细介绍请见第8章,届时还会介绍其他一些条件操作符。本章只介绍包含一个条件的查询。
操作符是SQL里的字符或关键字,用于连接SQL语句里的元素。
WHERE子句的语法如下所示:

下面是一个没有WHERE子句的简单SELECT语句:

现在对这个查询添加条件:

这里只显示了价格小于$5的记录。
下面这个查询显示了产品标识为119的产品描述和价格:

我们一般需要让输出以某种方式进行排序,为此可以使用ORDER BY子句,它能够以用户指定的列表格式对查询结果进行排列。ORDER BY子句的默认次序是升序,也就是说,如果对输出为字符的结果进行排序,就是A到Z的次序。反之,降序就是以Z到A的次序显示字符结果。对于数字值来说,升序是从1到9,降序是从9到1。
ORDER BY子句的语法是:

对前面某个范例语句进行扩展来体会如何使用ORDER BY子句。比如以升序(也就是字母顺序)对产品描述进行排序。注意其中使用的ASC选项,它可以在ORDER BY子句中的任意一个字段之后出现。

注意:排序方式
SQL排序是基于字符的ASCII排序。数字0~9会按其字符值进行排序,并且位于字母A到Z之前。由于数字值在排序时是被当作字符处理的,所以下面这些数字的排序是这样的:1、12、2、255、3。
下面的范例语句里使用了DESC,把输出结果按照反字母顺序显示:

注意:默认排序方式
由于升序是默认的排序方式,所以ASC选项并不需要明确指定。
SQL里存在着一些简化方式。ORDER BY子句里的字段可以缩写为一个整数,这个整数取代了实际的字段名称(排序操作中使用的一个别名),表示字段在关键字 SELECT 之后列表里的位置。
下面是在ORDER BY子句里使用整数表示字段的范例:

在这个查询里,整数1代表字段PROD_DESC,2代表PROD_ID,而3代表COST,以此类推。
在一个查询里可以对多个字段进行排序,这时可以使用字段名或相应的整数:

ORDER BY子句里的字段次序不一定要与关键字 SELECT之后的字段次序一致,如下所示:

ORDER BY子句里指定的字段次序决定了排序过程的完成方式。下面这个语句将首先对字段PROD_DESC进行排序,再对字段COST进行排序。

在使用 SQL 编写代码时,大小写敏感性是一个需要理解的重要概念。一般来说,SQL命令和关键字是不区分大小写的,也就是允许我们以大写或小写来输入命令和关键字,而且可以混用,大小写混用通常被称为驼峰命名法。关于大小写的问题请见第5章的介绍。
排序规则(collation)决定了 RDBMS 如何解释数据,包括排序方式和大小写敏感性等内容。数据的大小写敏感性很重要,这直接决定了WHERE子句如何匹配记录。用户务必要明确所用的RDBMS在排序规则方面的相关规定。在某些系统中,例如MySQL和Microsoft SQL Server,默认是大小写不敏感的。这就意味着,在进行数据匹配时,系统会忽视数据的大小写。也有一些系统,例如 Oracle,默认是大小写敏感的。这种系统在进行数据匹配时,需要考虑大小写情况。大小写敏感性取决于所用的数据库,因此在不同的系统中对查询的影响就会相应地有所不同。
注意:使用标准的大小写形式
在从数据库里获取数据时,必须在查询里使用与数据一致的大小写。此外,最好能够实施公司级别的大小写规则,确保在公司内部以统一的方式处理数据输入。
然而,对于在用户的RDBMS中确保数据的一致性来讲,大小写就是一个需要考虑的问题。在大多数情况下,数据在关系型数据库里似乎都是以大写形式保存的,以便保持数据的一致性。
举例来说,如果使用随意的大小写方式输入数据,数据的一致性就可能被破坏:

如果某人的姓被存储为smith,而我们在Oracle等大小写敏感的RDBMS中执行了如下查询,就不会得到返回结果:

下面的小节基于前面介绍的概念展示了查询的一些范例,首先是最简单的查询,然后逐步丰富它。在此我们使用表EMPLOYEE_TBL。
从表里选择全部记录,显示全部字段:

从表里选择全部记录,显示指定的字段:

注意:克服大小写敏感问题
在类似 Oracle 这样对大小写敏感的系统中,往往需要不断地核对数据,或者使用后续章节中介绍的SQL函数来修改数据,以便克服这类大小写问题。下面的范例演示了如何使用UPPER函数来改变WHERE子句所涉数据的大小写。
从表里选择全部记录,显示指定的字段。命令可以在一行输入,或是根据喜好使用软掉头:

从表里选择全部记录,显示多个字段:

显示满足指定条件的数据:

注意:确保所做的查询有约束条件
在从一个庞大的表里返回全部记录时,会得到大量的数据。
显示满足指定条件的数据,对输出结果进行排序:

显示满足指定条件的数据,根据多个字段进行排序,其中一个字段是逆序。在下面的范例中,EMP_ID以升序排列,而LAST_NAME 则以降序排列:

显示满足指定条件的数据,利用整数代替字段名来表示要排序的字段:

显示满足指定条件的数据,利用整数指定要排序的多个字段。字段的排序次序与它们在SELECT之后的次序并不相同:

利用一个简单的查询就可以了解表里的记录数量,或是某个字段里值的数量。统计工作是由函数COUNT完成的。虽然关于函数的内容要在本书稍后才有介绍,但在此引入这个函数是因为它经常出现在简单的查询之中。
COUNT函数的语法如下所示:

COUNT函数使用一对圆括号来指定目标字段,或是一个星号表示统计表里的全部记录。
注意:基本统计
如果被统计的字段是NOT NULL(必填字段),那么其值的数量就与表里记录的数量相同。但一般来说,我们使用COUNT(*)来统计表里的记录数量。
下面的语句可以统计表PRODUCTS_TBL里的记录数量:

下面的语句统计表PRODUCTS_TBL里字段PROD_ID的值的数量:

如果要统计表中特定列所出现的值的种类数,需要在COUNT函数中使用DISTINCT关键字。例如,如果要统计EMPLOYEE_TBL表的STATE列中不同值的种类数,需要使用如下查询:

要访问另一个用户的表,必须拥有相应的权限,否则就不能进行访问。在获得准许之后,我们可以从其他用户的表里获取数据(GRANT命令将在第20章介绍)。为了在SELECT语句里访问另一个用户的表,必须在表的名称之前添加规划名或相应的用户名,如下所示:

在进行某些查询时,我们使用字段别名来临时命名表的字段,其语法如下所示:

下面的范例显示了产品描述两次,并且给第二个字段一个别名:PRODUCT。请注意输出的字段标题。

注意:在查询中使用别名
如果要访问的表在数据库里有别名,就可以不必指定表的规划名。别名就是表的另一个名称,详细讨论请见第21章。
利用字段别名可以自定义字段的标题,在某些SQL实现里,字段别名还可以让我们用比较简洁的名称来引用某个字段。
注意:在查询中重新命名字段
当字段名称在SELECT语句里被重新命名时,其名称实际上并没有被修改,这种改变只在特定的SELECT语句里有效。
本章简单介绍了数据库查询的概念,这是从关系型数据库获取有用数据的手段。SELECT语句是一种数据查询语言(DQL)命令,用于在 SQL 里创建查询。每个 SELECT语句里都必须包含FROM子句。另外,利用WHERE子句可以为查询设置条件,利用ORDER BY子句可以为数据进行排序。本章介绍了编写查询语句的基础知识,后面的章节将进行更详细和深入的介绍。
问:为什么SELECT子句没有FROM子句就不行?
答:SELECT子句只是告诉数据库我们需要什么样的数据,而FROM子句告诉数据库到什么地方来获取这些数据。
问:在使用ORDER BY子句并设置为降序排序时,对数据到底有什么影响呢?
答:假设我们使用了 ORDER BY子句,并且从表 EMPLOYEE_TBL选择了字段 last_name。如果选择了降序排序,其次序就是从字母Z开始,到字母A结束。假设使用了ORDER BY子句,并且从表EMPLOYEE_PAY_TBL里选择的表示薪水的字段,这时选择降序排序就会从最高薪水开始,到最低薪水结束。
问:重新命名字段有什么好处?
答:新名称可以在特定报告中更好地描述所返回的数据。
问:下面语句的排序是什么?

答:查询会首先以COST字段进行排序,然后再以PROD_DESC进行排序。由于没有指定排序方式,所以两者都会是默认的升序。
下面的内容包含一些测试问题和实战练习。这些测试问题的目的在于检验对学习内容的理解程度。实战练习是为了把学习的内容应用于实践,并且巩固对知识的掌握。在继续学习之前请先完成测试与练习,答案请见附录C。
1.说出任何SELECT语句都需要的组成部分。
2.在WHERE子句里,任何数据都需要使用单引号吗?
3.SELECT语句属于SQL语言里的哪一类命令?
4.WHERE子句里能使用多个条件吗?
5.DISTINCT选项的作用是什么?
6.选项ALL是必需的吗?
7.在基于字符字段进行排序时,数字字符是如何处理的?
8.在大小写敏感性方面,Oracle与MySQL和Microsoft SQL Server有什么不同?
1.在计算机上运行RDBMS。使用数据库learnsql,输入以下SELECT命令。判断其语法是否正确,如果不正确就进行必要的修改。这里使用的是表EMPLOYEE_TBL。
a.

b.

c.

d.

e.

2.下面这个SELECT语句能工作吗?

3.编写一条SELECT语句,从表PRODUCTS_TBL里返回每件产品的名称和价格。哪个产品是最贵的?
4.编写一个查询,生成全部顾客及其电话号码的列表。
5.编写一个查询,生成具有某个特定姓的顾客的列表。尝试在 WHERE 子句中,使用混合大小写和全部大写两种方式。确定用户使用的RDBMS是否为大小写敏感。
本章的重点包括:
什么是操作符
SQL里操作符的概述
操作符如何单独使用
操作符如何联合使用
操作符用于在SELECT命令的WHERE子句中为返回的数据指定更明确的条件。SQL里有多种操作符,可以满足各种不同的查询需要。本章将介绍操作符的种类,以及如何在WHERE子句中正确使用操作符。
操作符是一个保留字或字符,主要用于SQL语句的WHERE子句来执行操作,比如比较和算术运算。操作符用于在SQL语句里指定条件,还可以联接一个语句里的多个条件。
本章要介绍的操作符包括:
比较操作符;
逻辑操作符;
求反操作符;
算术操作符。
比较操作符用于在SQL语句里对单个值进行测试。这里要介绍的比较操作符包括=、<>、<和>。
这些操作符用于测试:
相等;
不相等;
小于;
大于。
下面的小节会介绍这些比较操作符的含义与用法。
相等操作符在SQL语句里比较一个值与另一个值,等号(=)表示相等。在进行相等比较时,被比较的值必须完全匹配,否则就不会返回数据。如果相等比较过程中的两个值相等,那么这个比较的返回值就是TRUE,否则就是FALSE。这个布尔值(TRUE或FALSE)用于决定是否返回数据。
操作符=可以单独使用,也可以与其他操作符联合使用。请记住,字符数据的比较是否区分大小写,取决于用户RDBMS的相关设置。所以,用户务必需要了解所用数据库系统对数据的比较机制。
下面的范例表示薪水等于 20 000:

下面的查询会返回PROD_ID等于 2 345的全部数据:

有相等,就有不相等。在SQL里表示不相等的操作符是<>(一个小于号和一个大于号)。如果两个值不相等,条件就返回TRUE,否则就返回FALSE。
注意:不相等的表示方式
另一种表示不相等的方式是!=,而且很多主要的SQL实现采用这种方式。在Microsoft SQL Server、 MySQL和 Oracle中,两种方式是通用的。Oracle还提供了另一种方式,即^=操作符,但并不常用,因为大部分用户还是习惯于前两种方式。
下面的范例表示薪水不等于 20 000:

下面的范例显示产品标识不等于 2 345的全部产品信息:

再提醒一次,排序规则和系统的大小写敏感性直接决定了比较的结果。在大小写敏感的情况下,系统会认为CHAIN、Chain和chain是三个不同的值,结果也就自然会与用户的预期有所差异。
符号<(小于)和>(大于)可以自己使用,也可以与其他操作符联合使用。
下面的范例分别表示薪水小于或大于 20 000:

在第一范例里,任何小于且不等于 20 000的值会返回TRUE,大于或等于 20 000的值会返回FALSE。

在下面这个范例里,请注意值 24.99 并没有包含在结果集里,因为小于号并不包含所比较的值:

等号可以与小于号和大于号联合使用。
下面的范例表示薪水小于或等于 20 000:

下面的范例表示薪水大于或等于 20 000:

小于等于 20 000的值包括 20 000本身及任何小于 20 000的值,在这个范围内的值会返回TRUE,大于 20 000的值会返回FALSE。大于等于操作也同样包含 20 000这个值本身。

逻辑操作符用于对SQL关键字而不是符号进行比较。下面要介绍的逻辑操作符包括:
IS NULL;
BETWEEN;
IN;
LIKE;
EXISTS;
UNIQUE;
ALL和ANY。
这个操作符用于与NULL值进行比较。举例来说,对表EMPLOYEE_TBL里的PAGER字段搜索NULL值,就可以找到没有寻呼机的雇员。
下面是与NULL值进行比较的一个范例,这次是对薪水进行比较:

下面的范例展示如何从雇员表里找到没有寻呼机的全部雇员:

请注意,单词null与NULL值是不同的。观察下面这个范例:

操作符BETWEEN用于寻找位于一个给定最大值和最小值之间的值,这个最大值和最小值是包含在内的。
下面的范例表示薪水在 20 000与 30 000之间,而且包含 20 000和30 000:

注意:适当地使用BETWEEN
BETWEEN是包含边界值的,所以查询结果里会包含指定的最大值和最小值。
下面的范例表示价格在$5.95与$14.50之间的产品:

可以看出,值5.95和14.5也是包含在内的。
操作符 IN 用于把一个值与一个指定列表进行比较,当被比较的值至少与列表中的一个值相匹配时,它会返回TRUE。
下面的范例表示薪水必须等于 20 000、30 000或40 000中的一个值:

下面的范例展示利用操作符IN获取标识在指定范围内的产品记录:

使用操作符IN可以得到与操作符OR一样的结果,但它的速度更快。
操作符LIKE利用通配符把一个值与类似的值进行比较,通配符有两个:
百分号(%);
下划线(_)。
百分号代表零个、一个或多个字符,下划线代表一个数字或字符。这些符号可以复合使用。
下面的条件匹配任何以200开头的值:

下面的条件匹配任何包含200(在任意位置)的值:

下面的条件匹配第二和第三个字符是0的值:

下面的条件匹配以2开头,而且长度至少为3的值:

下面的条件匹配以2结尾的值:

下面的条件匹配第二个位置为2,结尾为3的值:

下面的条件匹配长度为5,以2开头,以3结尾的值:

下面的范例搜索产品描述以大写S结尾的记录:

下面的范例搜索产品描述中第二个字符是大写S的记录:

这个操作符用于搜索指定表里是否存在满足特定条件的记录。
下面的范例搜索表EMPLOYEE_TBL里是否包含EMP_ID为 333 333 333的记录:

下面是一个子查询的范例(详情请见第14章):

这个操作没有选中任何一条记录,因为表里不存在价格超过100的记录。
再看下面这个例子:

这一次显示了产品的价格,因为表里存在着价格小于100的记录。
操作符ALL用于把一个值与另一个集合里的全部值进行比较。
下面的范例测试薪水是否大于住在Indianapolis的全部雇员的薪水:

下面的范例展示操作符ALL如何与子查询联合使用:

这个输出表示有4条记录的价格大于那些价格小于10的所有记录。
操作符ANY用于把一个值与另一个列表里任意值进行比较。SOME是ANY的别名,它们可以互换使用。
下面的范例测试薪水是否大于住在Indianapolis的任意一名雇员的薪水:

下面的范例展示操作符ANY与子查询的联合使用:

这个输出结果中的记录比使用操作符ALL的多,因为这里只要求价格比小于10的价格中的任意一个高即可。价格为1.05的记录在此没有显示,因为它不大于比10小的价格中的任何一个。需要指出的是,ANY 与 IN 是不同的,IN 可以使用下面这样的表达式列表,而ANY不行:

另外,在后面介绍求反操作符时,我们会看到与 IN相反的是NOT IN,它相当于<>ALL,而不是<>ANY。
如果想在 SQL 语句里利用多个条件来缩小数据范围该怎么办呢?我们必须要组合多个条件,这正是连接操作符的功能。连接操作符包括:
AND;
OR。
连接操作符让我们可以在一个SQL语句里用多个不同的操作符进行多种比较。下面将介绍它们的功能。
操作符AND让我们可以在一条SQL语句的WHERE子句里使用多个条件。在使用AND时,无论SQL语句是事务操作还是查询,所有由AND连接的条件都必须为TRUE,SQL语句才会实际执行。
下面的范例表示EMPLOYEE_ID必须匹配 333 333 333,并且薪水必须等于 20 000:

下面的范例展示如何利用操作符AND来寻找价格在两个值之间的产品:

在这个输出里显示了价格大于10且小于30的产品。
下面的语句不会返回任何数据,因为任何产品都只有一个标识:

操作符OR可以在SQL语句的WHERE子句里连接多个条件,这时无论SQL语句是事务操作还是查询,只要OR连接的条件里有至少一个是TRUE,SQL语句就会执行。
下面的范例表示薪水必须匹配 20 000或30 000:

下面的范例展示了操作符OR的具体应用:

在这个输出结果里包含了满足任意一个条件的记录。
注意:比较操作符的灵活应用
比较操作符和逻辑操作符都可以单独或彼此复合使用。
在下面这个范例里使用了一个AND和两个OR,并且使用了圆括号来提高语句的可读性。

提示:提高查询的可读性
当 SQL 语句里包含多个条件和操作符时,利用圆括号把语句按照逻辑关系进行划分可以提高语句的可读性。当然,不恰当地使用圆括号也会影响输出结果。
这个输出结果中的记录必须是价格大于10,而且产品标识必须是列出的三个标识之一。PROD_ID为222的记录并没有返回,因为它的价格不大于10。圆括号不仅能够提高语句的可读性,还能够确保连接操作符能够正确地实现功能。在默认情况下,操作符是从左向右进行解析的。举例来说,寻找满足如下条件的记录:价格大于5,且PRODUCT_ID是222、90、11 235或 13中的一个。先来看一看下面这个查询返回的结果:

如果去掉其中的圆括号,就会发现返回的结果是不同的:

这时返回了FALSE PARAFFIN TEETH产品记录,因为现在的SQL查询条件是:PROD_ID等于 222且COST大于 5,或者任何PROD_ID等于 90、11 235或13的记录。在WHERE子句里正确地使用圆括号才能确保返回我们所需要的记录。如果不使用圆括号,系统通常会按照从左向右的顺序,依次对操作符进行处理。
对于前面讨论过的所有逻辑操作符,我们都可以颠倒它们的条件要求。
操作符NOT可以颠倒逻辑操作符的含义,它可以与其他操作符构成以下几种形式:
<> , != ( NOT EQUAL);
NOT BETWEEN;
NOT IN;
NOT LIKE;
IS NOT NULL;
NOT EXISTS;
NOT UNIQUE。
下面的小节将对它们分别加以介绍,首先来看如何测试不相等。
前面已经介绍了使用操作符<>来测试不相等,在此再介绍如何测试不相等的意义在于它实际上是对相等操作符的求反。下面的范例是在某些SQL实现里测试不相等的另一种方法。
下面的范例表示薪水不等于 20 000:

在第二个范例里使用了惊叹号对等号操作进行求反。在某些实现里,除了可以使用标准的<>表示不相等外,还可以用惊叹号。
注意:核实惊叹号的用法
关于惊叹号的使用请查看具体实现的帮助文档。这里介绍的其他操作符在各种SQL实现里一般是相同的。
注意:牢记BETWEEN的用法
操作符BETWEEN是包含边界值的,因此在前面这个范例里,价格等于5.95或14.50的记录就没有包含在结果里。
操作符BETWEEN的求反是这样的:

这表示薪水不能处于 20 000与 30 000之间,而且也不包含 20 000和 30 000。再看下面这个范例:

操作符 IN的求反是NOT IN,下面的条件表示薪水不在列表里的记录会被返回:

下面的范例展示了如何使用操作符IN的求反:

在这个输出里,标识属于操作符NOT IN之后的列表的记录没有被返回。
操作符LIKE的求反是NOT LIKE,这时只会返回不相似的值。
下面的条件表示不以200开头的值:

下面的条件表示不包含200(在任意位置)的值:

下面的条件表示在第二个位置不包含00的值:

下面的条件表示不是以2开始,且长度小于3的值:

下面的范例利用操作符NOT LIKE来显示一些值:

在这个输出结果里,不包括产品描述由字母L开始的记录。
操作符 IS NULL的求反是 IS NOT NULL,表示测试值不是NULL。下面的范例只返回NOT NULL的记录:

下面的范例利用操作符 IS NOT NULL返回呼机号不是空的雇员的记录:

操作符EXISTS的求反是NOT EXISTS。
下面的范例判断EMP_ID为 333 333 333的记录是否不在表EMPLOYEE_TBL里:

下面的范例展示了操作符NOT EXISTS与子查询的联合使用:

输出结果里显示了表里的最高价格,因为没有记录的价格高于100。
算术操作符用于在SQL语句里执行算术功能,这与其他大多数语言是一样的。传统的4个算术功能是:
+(加法);
-(减法);
*(乘法);
/(除法)。
加法是使用加号(+)来实现的。
下面的范例把每条记录的SALARY字段和BONUS字段相加来得到合计数值:

下面的范例返回SALARY和BONUS字段之和大于 40 000的全部记录:

减法是使用减号(-)实现的。
下面的范例计算SALARY字段减去BONUS字段的结果:

下面的范例返回SALARY与BONUS字段之差大于 40 000的全部记录:

乘法是使用星号(*)实现的。
下面的范例把SALARY字段乘以10:

下面的范例返回SALARY字段乘以 10之后大于40 000的全部记录:

下面范例里付款数额被乘以1.1,也就是把实际价格提高了10%:

除法是使用斜线(/)实现的。
下面的范例把SALARY字段除以10:

下面的范例返回SALARY字段大于 40 000的全部记录:

下面的范例返回SALARY字段除以 10之后大于40 000的全部记录:

算术操作符可以彼此组合使用,并且遵循基本算术运算中的优先级:首先执行乘法和除法,然后是加法和减法。用户控制算术运算次序的唯一方式是使用圆括号,圆括号里包含的表达式会被当作一个整体进行优先求值。
优先级是表达式在算术表达式里或与SQL内嵌函数结合时的求值次序。下表中的示例说明了优先级对计算结果的影响。

从下面的范例可以看出,如果表达式中只有乘法和除法,那么有没有圆括号和它们的位置都不会影响最终结果,这时优先级没有什么影响。但是,有些SQL实现可能在这种情况下并不遵循ANSI标准,当然,这也未必。

注意:确保表达式的准确性
在组合使用算术运算符时,一定要考虑到优先级的问题。语句中如果没有圆括号可能会导致不准确的结果,因为SQL语句本身的语法即使是正确的,其表示的逻辑也可能不正确。
下面是一些范例:

下面这个范例有点复杂:

由于没有使用圆括号,运算优先级的作用就发挥出来了,对BONUS的值进行了临时改变来进行条件判断。
本章介绍了SQL里的各种操作符,展示了它们的功能和作用,通过范例说明了这些操作符的单独使用及复合使用。介绍了基本的算术功能:加法、减法、乘法和除法。比较操作符可以测试相等、不相等、小于和大于关系,逻辑操作符包括BETWEEN、IN、LIKE、EXISTS和ALL。本章还展示了如何向SQL语句添加元素来指定更细致的条件,更好地控制SQL处理和获取数据的能力。
问:WHERE子句里能包含多个AND吗?
答:当然可以。事实上,任何操作符都可以多次使用,举例如下:

问:在WHERE子句里用单引号包围一个NUMBER类型的数据会怎么样呢?
答:查询仍然会执行。对于NUMBER类型的字段来说,单引号是没有必要的。
下面的内容包含一些测试问题和实战练习。这些测试问题的目的在于检验对学习内容的理解程度。实战练习有助于把学习的内容应用于实践,并且巩固对知识的掌握。在继续学习之前请先完成测试与练习,答案请见附录C。
1.判断正误:在使用操作符OR时,全部条件都必须是TRUE。
2.判断正误:在使用操作符IN时,所有指定的值都必须匹配。
3.判断正误:操作符AND可以用于SELECT和WHERE子句。
4.判断正误:操作符ANY可以使用一个表达式列表。
5.操作符IN的逻辑求反是什么?
6.操作符ANY和ALL的逻辑求反是什么?
7.下面的SELECT语句有错吗?错在何处?
a.

b.

c.

1.使用下面这个表CUSTOMER_TBL,编写一条SELECT语句,选择住在Indiana、Ohio、Michigan和Illinois并且姓名以字母A或B开头的客户,返回它们的ID和姓名(以字母顺序)。

2.使用下面这个表PRODUCTS_TBL,编写一个SQL语句,选择产品价格在$1.00与$12.50之间的产品,返回它们的ID、描述和价格。

3.如果在第2个练习题里使用了操作符BETWEEN,重新编写SQL语句,使用另一种操作符来得到相同的结果。如果没有使用BETWEEN,现在就来用一用。
4.编写一个SELECT语句,返回价格小于1.00或大于12.50的产品。有两种方法可以实现。
5.编写一个SELECT语句,从表PRODUCTS_TBL返回以下信息:产品描述、产品价格、每个产品5%的销售税。产品列表按价格从高到低排列。
6.编写一个SELECT语句,从表PRODUCTS_TBL返回以下信息:产品描述、产品价格、每个产品 5%的销售税、加上销售税的总价。产品列表按价格从高到低排列。有两种方法可以实现。
7.任选PRODUCTS_TBL表中的3种产品。编写一个查询,返回这3种产品的相关记录。之后,再重新编写一个查询,返回除这3种产品之外的所有产品记录。在查询中,组合使用相等操作符和连接操作符。
8.使用IN操作符重新编写练习题7中的查询。比较两种写法,哪种更高效?哪种更易读?
9.编写一个查询,返回所有名称以P开头的产品的记录。之后,再重新编写一个查询,返回所有名称不以P开头的产品的记录。
本章的重点包括:
什么是函数
如何使用函数
何时使用函数
使用汇总函数
使用汇总函数对数据进行合计
函数得到的结果
这一章介绍SQL的汇总函数,利用它们可以实现多种功能,例如获得销售数据的最高值,或者计算某一天提交的订单总数。汇总函数的真正用途将在下一章引入GROUP BY子句后进行介绍。
函数是SQL里的关键字,用于对字段里的数据进行操作。函数是一个命令,通常与字段名称或表达式联合使用,处理输入的数据并产生结果。SQL 包含多种类型的函数,本章介绍汇总函数。汇总函数为SQL语句提供合计信息,比如计数、总和、平均。
本章讨论的基本汇总函数包括:
COUNT;
SUM;
MAX;
MIN;
AVG。
下面的查询显示了本章里大多数范例所使用的数据:

下面的查询列出了表 EMPLOYEE_TBL 里的雇员信息,注意到其中有些雇员没有呼机号。

COUNT函数用于统计不包含NULL值的记录或字段值,在用于查询之中时,它返回一个数值。它也可以与DISTINCT命令一起使用,从而只统计数据集里不同的记录数量。命令ALL(与DISTINCT相反)是默认的,在语句中不必明确指定。在没有指定DISTINCT的情况下,重复的行也被统计在内。使用 COUNT 函数的另一种方式是与星号配合。COUNT(*)会统计表里的全部记录数量,包括重复的,也不管字段里是否包含NULL值。
注意:DISTINCT命令只能在特定情况下使用
DISTINCT命令不能与COUNT(*)一起使用,只能用于COUNT(column_name)。
COUNT函数的语法如下所示:

下面的范例统计全部雇员ID:

下面的范例只统计不相同的行:

下面的范例统计SALARY字段的全部行:

下面的范例统计表EMPLOYEE_TBL的全部行:

下面的范例使用COUNT(*)来获得表EMPLOYEE_TBL里的全部记录数量,结果是6。

注意:COUNT(*)返回的结果稍有不同
与其他形式相比,COUNT(*)返回的结果稍有不同。如果在COUNT函数中使用星号,将返回所有的统计数,包括重复项和NULL。这是一个很重要的差异。如果要统计某一字段的记录数,并且包括NULL,则需要使用ISNULL函数。
下面的范例使用COUNT(EMP_ID)来统计表里雇员标识的数量,返回的结果与前一个查询一样,因为全部雇员都有一个标识号。

下面的范例使用 COUNT(PAGER)统计具有呼机号的雇员数量,从结果可以看出只有两名雇员有呼机号。

表ORDERS_TBL的内容如下所示:

下面的范例统计表ORDERS_TBL里不同的产品标识数量:

PROD_ID为222的记录在表里有两条,因此产品标识不同的记录数量只有6而不是7。
注意:数据类型不影响统计结果
COUNT函数统计的是行数,不涉及数据类型。行里可以包含任意类型的数据。
SUM函数返回一组记录中某一个字段值的总和。它也可以与DISTINCT一起使用,这时只会计算不同记录之和。这一般没有什么意义,因为有些记录被忽略掉了。
SUM函数的语法如下所示:

注意:SUM函数只能处理数值型字段
SUM 函数所处理的字段类型必须是数值型的,不能是其他数据类型的,比如字符或日期。
下面的范例计算薪水的总和:

下面的范例计算不同薪水的总和:

下面的查询从表PRODUCTS_TBL里计算所有价格之和:

下面的范例使用了 DISTINCT 命令,其结果与前例相比有所差别,这也说明了为什么SUM函数很少使用DISTINCT。

下面的范例展示了虽然有些汇总函数要求使用数值型数据,但也有例外。这里使用了表EMPLOYEE_TBL里的PAGER字段,说明CHAR数据是可以隐含地转换为数值类型的:

如果数据不能隐含地转化为数值类型,其结果就是0。以LAST_NAME字段为例:

AVG函数可以计算一组指定记录的平均值。在与DISTINCT一起使用时,它返回不重复记录的平均值。AVG函数的语法如下所示:

注意:AVG函数只能处理数值型字段
AVG函数的参数必须是数值类型的。
下面的范例返回薪水的平均值:

下面的范例返回不同薪水的平均值:

下面的范例计算表PRODUCTS_TBL里COST字段全部值的平均值:

注意:查询结果的取舍
在某些实现里,查询结果可能会被取舍到相应数据类型的精度。
下面的范例在一个查询里使用两个汇总函数。有些雇员是按小时拿工资的,有些是拿月薪,所以我们使用两个函数来计算PAY_RATE和SALARY平均值。

MAX函数返回一组记录中某个字段的最大值,NULL值不在计算范围之内。DISTINCT也可以使用,但全部记录与不同记录的最大值是一样的,所以用DISTINCT没有意义。
MAX函数的语法如下所示:

下面的范例返回最高薪水:

下面的范例返回不同薪水中的最大值:

下面的范例返回表PRODUCTS_TBL里COST字段的最大值:

也可以对字符数据使用汇总函数,例如MAX和MIN。对于这种类型,排序规则再次发挥作用。通常,系统会将排序规则存入数据词典,查询结果会根据规则排序。在下面的范例中,我们对产品表的PRODUCT_DESC列使用MAX函数:

在这个范例中,函数根据数据词典返回了列中的最大值。
MIN 函数返回一组记录里某个字段的最小值,NULL 值不在计算之内。也可以使用DISTINCT,但由于全部记录与不同记录的最小值是一样的,所以用DISTINCT没有意义。
MIN函数的语法如下所示:

下面的范例返回最低薪水:

下面的范例返回不同薪水中的最小值:

下面的范例返回表PRODUCTS_TBL里COST字段的最小值:

警告:汇总函数与DISTINCT命令通常不一起使用
在汇总函数与DISTINCT命令一起使用时,查询返回的结果可能不是我们所需要的。汇总函数的目的在于根据表里的全部记录进行数据统计。
与MAX函数类似,MIN函数也可以根据数据词典,返回字符型数据的最小值。

下面的范例使用了汇总函数和算术操作:

这个语句统计了全部订单数量,统计了订购产品的总数,把这两个数值相除,就得到了每张订单上的平均产品数量。语句中还为计算创建了一个字段别名:AVG_QTY。
汇总函数十分有用,而且用法很简单。本章介绍了如何统计字段里的值、统计表里的记录数量、获取字段的最大值和最小值、计算字段值的总和、计算字段值的平均值。记住,在使用汇总函数时,NULL值是不被计算的,除非以COUNT(*)形式使用COUNT函数时。
汇总函数是本书中介绍的第一种SQL函数,后面会介绍更多的函数。汇总函数也可以用于分组值,详情在下一章介绍。大多数函数的语法是类似的,而且其用法是相当容易理解的。
问:在使用MAX或MIN函数时,为什么会忽略NULL值?
答:NULL值表示没有值。
问:在使用COUNT函数时,为什么数据类型是无关紧要的?
答:COUNT函数只统计记录数量。
下面的内容包含一些测试问题和实战练习。这些测试问题的目的在于检验对学习内容的理解程度。实战练习有助于把学习的内容应用于实践,并且巩固对知识的掌握。在继续学习之前请先完成测试与练习,答案请见附录C。
1.判断正误:AVG函数返回全部行里指定字段的平均值,包括NULL值。
2.判断正误:SUM函数用于统计字段之和。
3.判断正误:COUNT(*)函数统计表里的全部行。
4.下面的SELECT语句能运行吗?如果不行,应该如何修改?
a.

b.

c.

d.

e.

f.

1.利用表EMPLOYEE_TBL构造SQL语句,完成如下练习。
A.平均薪水是多少?
B.最高奖金是多少?
C.总薪水是多少?
D.最低小时工资是多少?
E.表里有多少行记录?
2.编写一个查询,来确定有多少雇员的姓以G开头?
3.编写一个查询,来确定系统中所有订单的总额。如果每个产品的价格是$10.00,全部订单的总额是多少?
4.如果所有雇员的姓名按照字母表排序,那么编写一个查询,来确定第一个和最后一个雇员的姓名是什么?
5.编写一个查询,对雇员姓名列使用AVG函数。查询语句能运行吗?思考为什么会产生这样的结果。
本章的重点包括:
为何想对数据进行分组
GROUP BY子句
分组估值函数
分组函数的使用方法
根据字段进行分组
GROUP BY与ORDER BY
HAVING子句
前面介绍了如何对数据库进行查询,并且以一种有组织的方式返回数据,还介绍了如何对查询返回的数据进行排序。这一章将介绍如何把查询返回的数据划分为组来提高可读性。
数据分组是按照逻辑次序把具有重复值的字段进行合并。举例来说,一个数据库包含关于雇员的信息,雇员住在不同的城市里,有些雇员住在同一个城市里。我们可能需要进行一个查询,了解每个指定城市里的雇员的信息。这时就是在根据城市对雇员进行分组,并且创建一个摘要报告。
假设我们想了解每个城市的雇员的平均薪水,这时可以对SALARY字段使用AVG函数(前一章介绍的),并且使用GROUP BY子句把结果按照城市进行分组。
数据分组是通过在SELECT语句(查询)里使用GROUP BY子句来实现的。上一章介绍了如何使用汇总函数,这一章将讨论如何联合使用汇总函数与GROUP BY子句,从而更高效地显示查询结果。
GROUP BY子句与 SELECT语句配合使用,把相同的数据划分为组。在 SELECT语句里,GROUP BY子句在WHERE子句之后,在ORDER BY子句之前。
GROUP BY子句在查询中的位置如下所示:

下面是包含了GROUP BY子句的SELECT语句的语法:

刚接触GROUP BY子句的时候,要养成按顺序书写的习惯,以确保逻辑正确。GROUP BY子句对CPU的运行效率有很大影响,如果我们不对提供给它的数据进行约束,那么后期很可能需要删除大量的无用数据。所以,需要使用WHERE子句来缩小数据范围,从而确保对有用的数据进行分组。
这里也可以加入ORDER BY子句,但RDBMS通常会使用GROUP BY子句中的列序对返回结果进行排序,本章后续内容将对此进行深入介绍。所以,除非用户对返回值的顺序有特殊要求,否则一般不会使用ORDER BY子句。但也有一些情况需要ORDER BY子句,比如用户在SELECT语句中、GROUP BY子句外使用了汇总函数,或者用户的RDBMS与相关标准有微小差异等。
下面的小节介绍GROUP BY子句在各种场合使用的范例。
典型的分组函数——也就是用于GROUP BY子句对数据进行划分的函数——包括AVG、MAX、MIN、SUM和COUNT。它们是第9章介绍的汇总函数,当时它们是用於单个值,现在它们将用于分组值。
数据分组是个简单的过程。被选中的字段(查询中 SELECT 之后的字段列表)才能在GROUP BY子句里引用;如果字段在SELECT语句里找不到,就不能用于GROUP BY子句。这当然是合乎逻辑的——如果数据根本就不显示,我们如何对其进行分组呢?
达到要求的字段名称必须出现在GROUP BY子句里。在GROUP BY子句里可以使用字段名称,也可以使用一个整数来代表字段,具体情况稍后介绍。在对数据进行分组时,分组字段的次序不一定要与SELECT子句里的字段次序相同。
SELECT语句在使用GROUP BY子句时必须满足一定条件。特别是被选中的字段必须出现在GROUP BY子句里,除了汇总函数。GROUP BY子句里的字段不必与SELECT子句里的字段具有相同的次序。只要SELECT子句的字段名称是符合条件的,它的名称就必须出现在GROUP BY子句里,下面来介绍一些使用GROUP BY子句的语法范例。
下面的SQL语句从表EMPLOYEE_TBL里选择字段EMP_ID和CITY,并且对返回的数据先根据CITY,再根据EMP_ID进行分组:

下面的SQL语句返回EMP_ID和SALARY字段的总和,然后根据薪水和雇员ID对数据进行分组:

下面的SQL语句从表EMPLOYEE_TBL里返回全部薪水的总和:

下面的SQL语句返回不同薪水的总和:

下面是使用一些实际数据的范例。在第一个范例里,我们可以看到表EMPLOYEE_TBL里包含3个不同的城市:

注意:GROUP BY子句中字段次序的特殊意义
注意观察SELECT语句里字段的次序,与GROUP BY子句里字段的次序进行对比。
下一个范例统计每个城市的记录数量。这时会分别看到每个不同城市的记录总和,因为其中使用了GROUP BY子句:

下面的查询针对一个临时表,它是基于表 EMPLOYEE_TBL 和 EMPLOYEE_PAY_TBL创建的。稍后我们就会介绍如何在一个查询中联合使用两个表。

下面的范例利用汇总函数 AVG 获得每个不同城市的平均小时工资和薪水。GREENWOOD和WHITELAND城市里没有平均小时工资,因为这两个城市里的雇员没有按小时支付的。

下面的范例组合多种查询元素来返回分组的数据。我们只想返回 INDIANPOLIS 和WHITELAND的平均小时工资和薪水。这时只能基于CITY进行分组,因为要对其他列使用汇总函数。最后对结果进行排序,首先是2,然后是3,即先是平均小时工资,然后是平均薪水。仔细研究下面的语句和输出结果。

具体数值在排序时位于NULL值之前,因此首先输出的是INDIANAPOLIS的记录。这里没有选择 GREENWOOD 字段,否则它的记录会显示在 WHITELAND 之前,因为GREENWOOD的平均薪水是$30 000(ORDER BY子句里第二排序是平均薪水)。
本小节最后一个范例组合使用MAX、MIN函数与GROUP BY子句:

像ORDER BY子句一样,GROUP BY子句里也可以用整数代表字段名称。下面就是这样一个范例:

这个 SQL语句返回雇员薪水的总和,根据雇员参加工作的年份进行分组。GROUP BY子句作用于整个结果集,其分组次序是1,代表YEAR(DATE_HIRE)。
GROUP BY和ORDER BY的相同之处在于它们都是对数据进行排序。ORDER BY子句专门用于对查询得到的数据进行排序,GROUP BY子句也把查询得到的数据排序为适当分组的数据,因此,GROUP BY子句也可以像ORDER BY子句那样用于数据排序。
用GROUP BY子句实现排序操作的区别与缺点是:
所有被选中的、非汇总函数的字段必须列在GROUP BY子句里;
除非需要使用汇总函数,否则使用GROUP BY子句进行排序通常是没有必要的。
下面的范例使用GROUP BY子句代替ORDER BY子句实现排序操作:

注意:错误信息的返回方式不同
不同的SQL实现返回错误信息的方式会有所不同。
在这个范例里,Oracle数据库返回一条错误信息,表示LAST_NAME不是一个GROUP BY 表达式。记住,SELECT 语句里列出的全部字段,除了汇总字段(使用汇总函数的)之外,全部都要出现在GROUP BY子句里。
下面的范例在GROUP BY子句里添加了完整的字段列表,从而解决了出现的问题:

这个范例从同一个表里选择相同的字段,但在GROUP BY子句里列出了SELECT子句里包含的全部字段,这时输出结果会依次按LAST_NAME、FIRST_NAME和CITY进行排序。虽然使用 ORDER BY子句能够更轻松地得到这种输出结果,但本例可以帮助我们更好地理解GROUP BY子句的工作方式,体会它必须首先对数据进行排序才能实现分组。
下面的范例对于表EMPLOYEE_TBL执行SQL语句,使用GROUP BY语句根据CITY进行排序:

注意这个范例里数据的次序,以及每个城市里LAST_NAME的次序。下面范例将统计表EMPLOYEE_TBL里的全部记录,结果会按照CITY进行分组,但是按每个城市的雇员数量进行排序。

注意观察结果显示的次序,它首先按照每个城市的雇员数量进行排序,然后才是按城市进行排序。前两个城市的统计数量都是 1,这对应于ORDER BY子句里的第一个表达式,所以这时再根据城市进行排序,GREENWOOD位于WHITELAND之前。
虽然GROUP BY和ORDER BY具有类似的功能,但它们有一个重要区别。GROUP BY子句用于对相同的数据进行分组,而ORDER BY子句基本上只用于让数据形成次序。GROUP BY和ORDER BY可以用于同一个SELECT语句里,但必须遵守一定的次序。
提示:不能在视图中使用ORDER BY子句
GROUP BY 子句可以用于在 CREATE VIEW 语句里进行数据排序,而ORDER BY子句不行。CREATE VIEW语句将在第 20章介绍。
在某些情况下,对分组数据进行小计是很有用的。例如,用户既要分析各种产品每年分别在不同国家的销售数据,也需要看到每年在每个国家所有产品的销售数据总额。ANSI SQL提供了CUBE和ROLLUP语句来解决这类问题。
ROLLUP语句可以用来进行小计,即在全部分组数据的基础上,对其中的一部分进行汇总。其ANSI语法结构如下:

ROLLUP 语句的工作方式是这样的,在完成了基本的分组数据汇总以后,按照从右向左的顺序,每次去掉字段列表中的最后一个字段,再对剩余的字段进行分组统计,并将获得的小计结果插入返回表中,被去掉的字段位置使用NULL填充。最后,再对全表进行一次统计,所有字段位置均使用NULL填充。Microsoft SQL Server和Oracle使用ANSI标准语法,但MySQL的语法结构稍有不同:

下面首先来看一个简单的GROUP BY语句返回的结果,其中我们根据城市和邮编来获得平均工资:

下面的范例使用了ROLLUP语句来获得小计数据:

注意观察返回结果,我们在完成了基本的分组数据汇总以后,去掉了最后一个字段(邮编),并根据剩余的字段(城市),再次进行分组统计,并将结果插入返回表。最后,还对全表进行了一次统计。
CUBE语句的工作方式与此不同。它对分组列表中的所有字段进行排列组合,并根据每一种组合结果,分别进行统计汇总。最后,CUBE语句也会对全表进行统计。CUBE语句的语法结构如下:

CUBE语句的性质独特,因此通常被用来生成交叉报表。例如,如果需要根据城市、州、地区三个字段获得销售数据的分组统计结果,GROUP BY CUBE语句会根据以下每一种字段组合进行分组汇总,并产生统计结果。

CUBE语句在Microsoft SQL Server和Oracle中都可以使用,但在本书成稿之时,MySQL尚不支持该语句。下面的范例演示了如何使用CUBE语句:

从上述范例我们可以看到,由于要根据分组列表中提供的所有字段的各种组合分别进行统计汇总,使用CUBE语句要返回的记录数会大大增加。
HAVING子句在SELECT语句里与GROUP BY子句联合使用时,用于告诉GROUP BY子句在输出里包含哪些分组。HAVING对于GROUP BY的作用相当于WHERE对于SELECT的作用。换句话说,WHERE子句设定被选择字段的条件,而HAVING子句设置GROUP BY子句形成分组的条件。因此,使用HAVING子句可以让结果里包含或是去除整组的数据。
下面是HAVING子句在查询里的位置:

HAVING子句必须跟在GROUP BY子句之后、在ORDER BY子句之前。
下面是SELECT语句在包含HAVING子句时的语法:

下面的范例选择除了GREENWOOD之外所有城市的平均小时工资和薪水。输出结果按照CITY进行分组,但只显示平均薪水超过$20 000的分组(城市),并且按照每个城市的平均薪水进行排序。

为什么这个查询只返回了一行结果?
WHERE子句把城市GREENCITY排队在外。
INDIANAPOLIS的平均薪水只有$20 000,没有超过$20 000,所以也不在输出结果里。
本章介绍了如何使用GROUP BY子句对查询结果进行分组。GROUP BY子句主要与汇总函数配合使用,比如SUM、AVG、MAX、MIN和COUNT。GROUP BY的本质与ORDER BY类似,也是对查询结果进行排序。GROUP BY对结果进行逻辑上的分组排序,虽然也可以实现单纯的数据排序,但就不如使用ORDER BY方便了。
HAVING子句是GROUP BY子句的一个扩充,用于对分组添加条件。相比之下,WHERE子句用于给查询的SELECT子句添加条件。下一章将介绍一些新的函数,进一步控制查询的结果。
问:当SELECT语句里使用ORDER BY子句时,是否一定要使用GROUP BY子句?
答:不是。GROUP BY子句完全是任选的,但它与ORDER BY配合使用时会发挥很大的作用。
问:分组值是什么?
答:以表EMPLOYEE_TBL里的CITY字段为例。如果选择雇员的姓名与城市,然后把输出按照城市进行分组,那么相同的城市就会被分在一组。
问:如果想利用GROUP BY子句根据某字段进行分组,该字段是否一定要出现在SELECT语句里?
答:是,字段必须出现在SELECT语句里,GROUP BY子句才能使用它。
下面的内容包含一些测试问题和实战练习。这些测试问题的目的在于检验对学习内容的理解程度。实战练习有助于把学习的内容应用于实践,并且巩固对知识的掌握。在继续学习之前请先完成测试与练习,答案请见附录C。
1.下面的SQL语句能正常执行吗?
a.

b.

c.

d.

2.判断正误:在使用HAVING子句时一定也要使用GROUP BY子句。
3.判断正误:下面的SQL语句返回分组的薪水总和:

4.判断正误:被选中的字段在GROUP BY子句里必须以相同次序出现。
5.判断正误:HAVING子句告诉GROUP BY子句要包括哪些分组。
1.运行数据库,输入如下查询来显示表EMPLOYEE_TBL里的全部城市:

2.输入如下查询,把结果与练习1的结果进行比较:

3.HAVING子句与WHERE子句的相似之处在于都可以指定返回数据的条件。WHERE子句是查询的主过滤器,而HAVING子句是在GROUP BY子句对数据进行分组之后进行过滤。输入如下查询来了解HAVING子句的工作方式:

4.修改练习3里的查询,把结果按降序排序,也就是数值从大到小。
5.编写一个查询,从表EMPLOYEE_PAY_RATE里列出每个城市的平均税率和工资。
6.编写一个查询,从表EMPLOYEE_PAY_RATE里列出城市平均薪水高于$20 000的每个城市的平均薪水。
本章的重点包括:
字符函数简介
如何及何时使用字符函数
ANSI SQL函数范例
常见实现的特定函数范例
转换函数概述
如何及何时使用转换函数
本章介绍如何使用函数来调整输出结果的外观,有些是 ANSI 标准函数,有些是基于该标准的函数,还有一些是由主要的SQL实现所使用的函数。
注意:ANSI标准并不是绝对不变的
书中介绍的ANSI概念只是概念而已。ANSI规定的标准只是对如何在关系型数据库里使用 SQL 的一个方针,因此书中介绍的某些函数与用户所用SQL 实现里的不一定相同。它们的概念是相同的,工作方式一般也是一样的,但函数名称和实际的语法可能不同。
字符函数用于在 SQL 里以不同于存储方式的格式来表示字符串。本章的第一部分讨论ANSI的字符函数概念,第二部分介绍使用不同SQL实现的函数用于实际操作。最常用的ANSI字符函数主要用于进行串接、子串和TRANSLATE等操作。
串接就是把两个单独的字符串组合为一个。举例来说,可以把个人的姓和名串接在一起形成一个字符串来表示完整的姓名。
JOHN与SMITH串接起来就得到 JOHN SMITH。
子串的概念就是从字符串里提取一部分。比如下面的值都是JOHNSON的子串:
J;
JOHN;
JO;
ON;
SON。
TRANSLATE函数用于逐字符地把一个字符串变换为另一个,它通常有3个参数:要被转换的字符串、要转换的字符列表、代入字符的列表。稍后将介绍一些实际的范例。
字符函数主要用于对字段里的字符串或值进行比较、连接、搜索、提取片断等,可用的字符函数有很多。
下面的小节介绍当前主要SQL厂商对ANSI概念的实现,包括Microsoft SQL Server、MySQL和Oracle。
串接及其他一些函数在不同实现里略有不同。下面的范例展示了在Oracle和SQL Server里的串接操作。
假设要把JOHN和SON串接起来形成JOHNSON。在Oracle里的代码是这样的:

在SQL Server里的代码是这样的:

在MySQL里的代码是这样的:

总的来说,串接操作在Oracle里的语法是:

在SQL Server里的语法是:

在MySQL里的语法是:

MySQL 和 Oracle 中都有串接函数,用来把两个字符串连接起来,其作用相当于 SQL Server中的“+”和Oracle中的“||”。区别在于,Oracle中的串接函数只能用于两个字符串,而 MySQL 中的串接函数可以连接多个字符串。需要注意的一点是,串接函数用于连接字符串,如果要连接数字,则需要将数字首先转换为字符串。遗憾的是,Microsoft SQL Server不支持串接函数。以下是进行串接操作的一些范例。
下面的 SQL Server语句把城市与州字段的值串接在一起,并且在两个值之间放置一个逗号:

下面的Oracle语句把城市与州字段的值串接在一起,并且在两个值之间放置一个逗号:

这个操作在Oracle中无法使用串接函数完成,因为它连接了多个字符串。
注意:对字符串使用引号
注意前面这个 SQL 语句里单引号与逗号的使用。绝大多数字符和符号都可以被包围在单引号里。有些实现可能使用双引号来表示直义字符串。
下面的 SQL Server语句把城市与州字段的值串接在一起,并且在两个值之间放置一个空格:

下面的SQL Server语句把个人的姓和名串接在一起,并且在两个值之间放置一个逗号:

TRANSLATE函数搜索字符串里的字符并查找特定的字符,标记找到的位置,然后用替代字符串里对应的字符替换它。其语法如下所示:

下面的SQL语句把字符串里每个I都替换为A,每个N都替换为B,每个D都替换为C:

下面的范例把TRANSLATE用于实际的数据:

在这个范例里,所有的I都被替换为A、N替换为B、D替换为C。在INDIANAPOLIS里, IND被替换为ABC,在GREENWOOD里,D被替换为C。WHITELAND的替换也是如此。
MySQL和Oracle都支持使用TRANSLATE函数,但是Microsoft SQL Server还不支持。
REPLACE函数用于把某个字符或字符串替换为指定的一个字符(或多个字符),其使用类似于TRANSLATE函数,只是它是把一个字符或字符串替换到另一个字符串里,其语法是:

下面的语句返回全部的名,并且把全部的T都替换为B:

下面的语句返回雇员表里的全部城市,并且把城市名称里的I都替换为Z:

Microsoft SQL Server、MySQL和Oracle全都支持该函数的ANSI语法结构。
大多数实现都提供了控制数据大小写的函数。UPPER函数可以把字符串里的小写字母转化为大写。
语法如下所示:

下面的SQL语句把字段里所有的字符都转化为大写:

Microsoft SQL Server、MySQL和Oracle全都支持该函数。在MySQL中,还有一个UCASE函数可以实现同样的操作,由于功能相同,用户最好还是遵循ANSI标准语法。
与UPPER函数相反,LOWER把字符串里的大写字符转化为小写。
其语法如下所示:

下面的语句把字段里所有的字符都转化为小写:

Microsoft SQL Server、MySQL和Oracle全都支持该函数。与UPPER函数类似,MySQL中也存在一个LCASE函数,但用户最好还是遵循ANSI标准语法。
在大多数 SQL 实现里都有获取字符串子串的函数,但名称可能略有不同,比如 Oracle和SQL Server。
在Oracle里的语法是:

在SQL Server里的语法是:

对于这个函数来说,这两个实现之间的唯一差别就是函数的名称。
下面的SQL语句返回EMP_ID的前3个字符:

下面的SQL语句返回EMP_ID的第4个和第5个字符:

下面的SQL语句返回EMP_ID的第6个到第9个字符:

下面的范例在SQL Server和MySQL里都可以使用:


下面的SQL语句是用于Oracle的:

注意:不同实现的反馈信息有所差异
注意最后两个查询的反馈信息。前一个是“6 rows affected”,后一个是“6 rows selected”。在不同的SQL实现里都会看到类似这样的差别。
INSTR函数用于在字符串里寻找指定的字符集,返回其所在的位置。语法如下所示:

下面的SQL语句返回表EMPLOYEE_TBL里每个州名里字母I第一次出现的位置:

下面的SQL语句查找字母A在字段PROD_DESC里第一次出现的位置:

可以看到,如果字符串里不存在字母A,返回的位置值是0。
INSTR 在 MySQL 和 Oracle 中有效,但在 Microsoft SQL Server 中,则需要使用CHARINDEX函数。
LTRIM函数是另一种截取部分字符串的方式,它与SUBSTRING属于同一家族。LTRIM用于从左剪除字符串里的字符,其语法如下所示:

下面的SQL语句从所有LESLIE的左侧剪除LES:

下面的SQL语句返回职位以及职位字符串里从左侧剪除SALES之后的结果:

SHIPPER里的S也被剪除掉了,虽然SHIPPER里并不包含字符串SALES。SALES里前4 个字符被忽略掉了,被搜索的字符必须以相同次序出现在目标字符串里,而且必须位于目标字符串的最左侧。换句话说,LTRIM会剪除被搜索的字符串在目标字符串里最后一次出现位置之左的全部字符。
Microsoft SQL Server、MySQL和Oracle全都支持该函数。
类似于LTRIM,RTRIM也用于剪除字符,但它是剪除字符串的右侧。其语法如下所示:

下面的SQL语句返回名为BRANDON的,并且剪除右侧的ON,留下BRAND作为结果:

这个SQL语句返回表PAY_TBL里的职位列表,并且把职位字符串最右侧的ER剪除掉:

全部符合条件的字符串里最右侧的ER都被剪除了。
Microsoft SQL Server、MySQL和Oracle全都支持该函数。
DECODE函数不是ANSI标准里的,至少目前还不是,但它具有强大的功能。该函数主要用于Oracle和PostgreSQL。它可以在字符串里搜索一个值或字符串,如果找到了,就在结果里显示另一个字符串。
其语法如下所示:

下面的查询在表 EMPLOYEE_TBL 里搜索全部姓,如果找到 SMITH,就会在结果里显示JONES,否则就显示OTHER(语句中设置的默认值):

下面的范例对表EMPLOYEE_TBL里的CITY字段使用DECODE:

从输出结果可以看到,INDIANAPOLIS显示为INDY,GREENWOOD显示为GREEN,而其他城市显示为OTHER。
下面的小节介绍其他一些值得一提的函数,它们在主流SQL实现里也是很常见的。
LENGTH函数是很常见的,用于得到字符串、数字、日期或表达式的长度,单位是字节。其语法如下所示:

下面的SQL语句返回产品描述及其长度:

MySQL和Oracle都支持该函数。而Microsoft SQL Server则使用LEN函数来实现相同的功能。
IFNULL函数用于在一个表达式是NULL时从另一个表达式获得值。它可以用于大多数数据类型,但值与替代值必须是同一数据类型。其语法如下所示:

下面的SQL语句寻找NULL值,并且用 999 999 999代替NULL值:

只有NULL值被替换为 999 999 999。
只有MySQL支持该函数。要实现相同的功能,Microsoft SQL Server使用 ISNULL函数,而Oracle则使用COALESCE函数。
COALESCE函数也是用指定值替代NULL值,这一点与IFNULL是一样的。其不同点在于,它可以接受一个数据集,依次检查其中每一个值,直到发现一个非NULL值。如果没有找到非NULL值,它会返回一个NULL值。
下面的范例用COALESCE函数返回BONUS、SALARY和PAY_RATE字段里第一个非NULL值。

Microsoft SQL Server、MySQL和Oracle全都支持该函数。
LPAD(左填充)用于在字符串左侧添加字符或空格,其语法如下所示:

下面的范例在每个产品描述左侧添加句点,使其总长度达到30个字符:

MySQL和Oracle全都支持该函数。遗憾的是,Microsoft SQL Server中没有对应的函数。
RPAD(右填充)在字符串右侧添加字符或空格,其语法如下所示:

下面的范例在每个产品描述的右侧添加句点,让总长度达到30个字符:

MySQL和Oracle全都支持该函数。遗憾的是,Microsoft SQL Server中没有对应的函数。
ASCII函数返回字符串最左侧字符的“美国信息交换标准码(ASCII)”,其语法如下所示:

下面是一些范例:
ASCII(‘A’)返回65;
ASCII(‘B’)返回66;
ASCII(‘C’)返回67;
ASCII(‘a’)返回95。
更多信息请参见www.asciitable.com上的ASCII表。
Microsoft SQL Server、MySQL和Oracle全都支持该函数。
在多个不同实现之间,算术函数是相对比较标准的。算术函数可以对数据库里的值根据算术规则进行运算。
最常见的算术函数包括:
绝对值(ABS)
舍入(ROUND)
平方根(SQRT)
符号(SIGN)
幂(POWER)
上限和下限(CEIL、FLOOR)
指数(EXP)
SIN、COS、TAN
大多数算术函数的语法是:

Microsoft SQL Server、MySQL和Oracle都支持所有的算数函数。
转换函数把数据类型从一种转换为另一种。举例来说,我们的数据通常是以字符形式保存的,但为了计算就需要把它转换为数值。算术函数和计算不能用于以字符形式表示的数据。
下面是一些常见的数据转换:
字符到数字;
数字到字符;
字符到日期;
日期到字符。
本章将介绍前两种转换,其他的转换在第12章介绍。
数值数据类型与字符串数据类型有两个主要的区别:
算术表达式和函数可以用于数值;
在输出结果里,数值是右对齐的,而字符串是左对齐的。
注意:转换为数值
对于要转换为数值的字符串来说,其中的字符必须是 0~9。另外加号、减号和句点可以分别用来表示正数、负数和小数。举例来说,字符串“STEVE”不能转化为数值,而个人的社会保险号码能够以字符串形式保存,并可以利用转换函数方便地转换为数值。
当字符串转化为数值时,它就具有了上述两个特点。
有些实现没有把字符串转化为数值的函数,有些有。无论是何种情况,请查看相应的文档来了解转换的语法和规则。
注意:某些实现的自动转换
有些实现在需要时会隐含进行数据类型转换,这意味着系统会自动进行转换,这时就不必使用转换函数了。详细情况请参考具体实现的帮助文档。
下面是使用Oracle转换函数的一个数值转换范例:

雇员标识在转换后就变成右对齐的。
与前面的转换相比,数值转换为字符串是个完全相反的过程。
下面的范例使用SQL Server里的转换函数把数值转换为字符串:

提示:不同数据类型的对齐方式不同
数据的对齐方式是判别字段数据类型的最简单方式。
下面的范例使用Oracle的函数实现完全相同的转换:

大多数函数可以在 SQL 语句里组合使用。如果不允许函数组合使用,SQL 就会有很大的局限性。下面的范例在一个查询里组合使用两个函数(串接和子串),把 EMP_ID 字段分为3部分,再用短划线把它们连接起来,从而得到更清晰易读的社会保险号码。范例里使用了CONCAT函数来组合字符串。

下面的范例使用LENGTH函数和算术运算符(+)把每个字段的姓和名的长度加在一起,然后SUM函数返回所有姓和名的长度之和。

注意:内嵌函数的处理
当SQL语句的函数内部嵌有函数时,最内层的函数首先被处理,然后从里向外依次执行各个函数。
到目前为止,我们介绍了在SQL语句(通常是个查询)里使用多种函数来调整或强化输出结果的外观。这些函数包括字符函数、算术函数和转换函数。需要明确的是,ANSI 标准是如何实现SQL的一个方针,但没有规定准确的语法或位置限制。大多数厂商提供了标准函数,并且遵循ANSI标准,但也都有自己的函数。函数名和语法可能有所不同,但其概念都是相同的。
问:所有的函数都符合ANSI标准吗?
答:不,并不是所有函数都严格遵守ANSI标准。函数像数据类型一样,经常是取决于具体实现的。大多数实现具有ANSI函数的超集,有些实现包含了广泛的函数来扩展功能,而有些则有所局限。本章展示了一些常用实现的函数范例,但在具体使用时,由于很多实现具有类似的函数(但可能略有区别),用户应该查看相应的文档来了解可用的函数及其用法。
问:在使用函数时,数据库里的数据是否实际发生了改变?
答:没有。在使用函数时,数据库里的数据没有改变。函数通常是用在查询语句里来调整输出的外观。
下面的内容包含一些测试问题和实战练习。这些测试问题的目的在于检验对学习内容的理解程度。实战练习有助于把学习的内容应用于实践,并且巩固对知识的掌握。在继续学习之前请先完成测试与练习,答案请见附录C。
1.匹配函数与其描述。
描述 函数
a.从字符串里选择一部分 ||
b.从字符串左侧或右侧剪切字符串 RPAD
c.把全部字符都改变为大写 LPAD
d.确定字符串的长度 RTRIM
e.连接字符串 UPPER LTRIM LENGTH LOWER SUBSTR
2.判断正误:在 SELECT 语句里使用函数调整数据输出外观时会影响数据库里存储的数据。
3.判断正误:当查询里出现函数嵌套时,最外层的函数会首先被处理。
1.在mysql>提示符下输入如下命令,把每个雇员的姓和名连接起来:

在Oracle和SQL Server中如何实现该语句?
2.输入以下MySQL命令,显示每个雇员的完整姓名和电话区号:

在Oracle和SQL Server中如何实现该语句?
3.编写一个SQL语句,列出雇员的电子邮件地址。电子邮件地址并不是数据库里的一个字段,雇员的电子邮件地址应该由以下形式构成:

举例来说,John Smith的电子邮件地址是 JOHN.SMITH@PERPTECH.COM。
4.编写一个SQL语句,以如下形式列出雇员的姓名、ID和电话号码。
a.姓名显示为SMITH, JOHN;
b.雇员ID显示为999-99-9999;
c.电话号码显示为(999)999-9999。
本章的重点包括:
理解日期和时间
日期和时间是如何存储的
典型的日期和时间格式
如何使用日期函数
如何使用日期转换
本章介绍 SQL 中的日期和时间,不仅要详细讨论 DATETIME 数据类型,还会讨论某些实现如何使用日期、如何从期望的格式中提取日期和时间,以及其他一些常见规则。
注意:SQL的不同实现
众所周知,SQL的实现有多种。本书介绍ANSI标准及最常见的非标准函数、命令和操作符。本书的范例使用 MySQL,但即使是在 MySQL里,日期的保存格式也有多种,用户必须查看相应的文档来了解实际情况。但无论以何种格式存储日期,SQL实现中都有转化格式的函数。
每个实现都有一个默认的日期和时间存储格式,但这种默认格式一般是不同的。下面的小节首先复习 DATETIME 数据格式的标准格式及其元素,然后介绍某些流行 SQL 实现中的日期和时间数据类型,包括Oracle、MySQL和Microsoft SQL Server。
日期和时间(DATETIME)存储的标准SQL数据类型有3种。
DATE:直接存储日期。DATE 的格式是 YYYY-MM-DD,范围是从 0001-01-01 到9999-12-31。
TIME:直接存储时间。TIME 的格式是 HH:MI:SS.nn…,范围是从 00:00:00…到23:59:61.999…。
TIMESTAMP:直接存储日期和时间。TIMESTAMP 的格式是 YYYY-MM-DD HH:MI:SS.nn…,范围是从 0001-01-01 00:00:00…到 9999-12-31 23:59:61.999…。
DATETIME元素是属于日期和时间的元素,包含在DATETIME定义里。下面列出了必须有的DATETIME元素及其取值范围。

每一种元素都是我们日常会遇到的。秒是以小数表示的,允许表达式的值是十分之一秒、百分之一秒、毫秒等。有些人可能要问一分钟会超过60秒吗?根据ANSI标准,61.999秒用于插入或略去闰秒,而这是很少发生的事情。不同实现中日期和时间的存储可能差别很大,用户请参考具体实现的文档。
注意:闰年由数据库处理
如果数据以DATETIME格式存储在数据库里,像闰秒和闰年这样的日期调整是由数据库在内部完成的。
像其他数据类型一样,每种实现都有自己的形式和语法来处理日期和时间。表12.1介绍了 3种实现(Microsoft SQL Server、MySQL和Oracle)的日期和时间。
表12.1 不同平台的日期类型


续表

日期函数在每个不同实现里是有所区别的。类似于字符串函数,日期函数用于调整日期和时间数据的外观,以适当的方式显示日期和时间数据、进行比较、计算日期之间的间隔等。
注意:日期和时间类型在不同的实现中有所不同
每种实现都有自己的数据类型来存储日期和时间信息,但大多数实现遵循ANSI标准,也就是说日期和时间的全部元素都保存在相关的数据类型里。日期实际的存储方式是取决于具体实现的。
有人可能已经产生问题了:如何从数据库获取当前日期呢?在很多情况下都可能需要从数据库获取当前日期,最常见的是用来与存储的日期进行比较,或是作为某种时间标记。
从根本上来说,当前日期保存在数据库所在的计算机上时,被称为系统日期。数据库通过与操作系统进行交互可以获取系统日期,从而用于自身需要或是满足数据库请求(比如查询)。
下面介绍几种不同实现里获取系统日期的一些方法。
Microsoft SQL Server使用名为GETDATE()的函数获取系统日期,其使用方法及返回值如下所示:

MySQL使用NOW函数获取当前日期和时间。NOW被称为伪字段,因为它具有像其他字段一样的行为,能够从数据库里的任意表里被选择,但它实际上并不存在于任何表的定义里。
下面是使用MySQL语句获取当前日期和时间的范例:

Oracle使用SYSDATE函数,以下范例使用了Oracle中的DUAL表:

在处理日期和时间信息时,可能要考虑时区。举例来说,美国中部时间下午6:00并不等同于澳大利亚的同一时间。另外,在使用夏时制的地区,每年都要调整两次时间。如果在维护数据时需要考虑时区问题,我们就需要处理时区和进行时间转换(如果SQL实现里有这样的函数)。
下面是一些常见时区及其缩写。

下面是在某个给定时间不同时区之间的差别:

注意:处理时区
有些实现里包含了能够处理时区的函数,但并不是所有实现都支持使用时区,实际应用时要考虑特定的实现及需求。
日、月以及时间的其他组成部分可以加到日期上,从而进行日期比较或是在WHERE子句里提供更精确的条件。
DATETIME值可以增加时间间隔。根据标准的定义,时间间隔用于调整DATETIME值,如下例所示:

下面是使用SQL Server的DATEADD函数的范例:

下面是使用Oracle的ADD_MONTHS函数的范例:

在Oracle里,如果想向日期上增加一天,处理方式如下所示:

如果想在MySQL里进行同样的查询,可以使用ANSI标准的INTERVAL命令,如下所示。如果使用像Oracle那样的方式,MySQL会把日期转换为整数再进行加法运算。


从MySQL、SQL Server和Oracle的这些范例可以看出,虽然它们从句法上都与ANSI标准有所区别,但其结果都是基于SQL标准所描述的同一概念。
表 12.2列出了SQL Server、Oracle和MySQL里其他一些日期函数。
表12.2 不同平台的日期函数

很多原因都会导致进行日期转换,主要用于转换定义为 DATETIME 的数据类型,或是具体实现中其他的数据类型。
进行日期转换的典型原因有:
比较不同数据类型的日期值;
把日期值格式化为字符串;
把字符串转化为日期格式。
ANSI的CAST操作符可以把一种数据类型转换为另一种,其基本语法如下所示:

下面的小节会介绍一些特定实现中的具体语法,包括:
DATETIME值里元素的表示;
日期转化为字符串;
字符串转化为日期。
日期描述由格式元素组成,用于从数据库以期望的格式提取日期和时间信息。日期描述并不是在所有实现里都存在。
如果不使用日期描述和某种转换函数,日期和时间信息从数据库里是以默认格式提取的,如下所示:

如果我们想以如下方式显示日期该怎么办呢?

这时我们不得不把日期从DATETIME模式转化为字符串,这是由一些专用函数完成的,稍后将加以介绍。
表12.3展示了很多实现里所使用的常见日期元素,它们可以帮助我们从数据库里获取适当的日期时间信息。
表12.3 常见日期元素

续表

注意:上表所列是MySQL里最通用的日期元素,不同版本的MySQL里还有其他一些可以使用的日期元素。
日期转换为字符串是为了改变日期在查询中的输出形式,它是通过使用转换函数实现的。下面展示了两个把日期和时间数据转换为字符串的范例,首先是使用SQL Server:

第二个范例是Oracle,它使用TO_CHAR函数:

下面的范例展示了在一个 MySQL 实现里把字符串转化为日期格式。在转换完成之后,数据可以保存到定义为某种DATETIME数据类型的字段里。

有人也许会问,前例中只提供了一个日期值,为什么会显示有6条记录被选择呢?这是因为被转换的字符串来自于表EMPLOYEE_PAY_TBL,而它有6行数据。
在Microsoft SQL Server中,我们使用CONVERT函数:

本章介绍了DATATIME值是基于ANSI标准的,但就像很多SQL元素一样,大多数实现偏离了标准 SQL 命令的名称与语法,但其表示与操作日期和时间信息的基本概念没有改变。前一章介绍了函数在不同实现里的区别,而这一章介绍了日期和时间类型、函数和操作符之间的不同。记住,在此介绍的范例并不是在所有SQL实现里都能执行的,但其基本概念是相同的,适用于任何实现。
问:不同实现为什么与数据类型和函数的单一标准集有所差别?
答:不同实现在数据类型和函数外观方面有所区别,主要是因为不同的厂商使用不同的方式保存数据,追求用最有效的方式提供数据检索。但是,所有实现都应该根据ANSI描述的必要元素来提供保存日期和时间的相同方式,比如年、月、日、小时、分钟、秒等。
问:如果想用不同于实现所提供的方式来保存日期和时间信息,应该怎么办呢?
答:如果把保存日期的字段定义为变长字符串类型,我们几乎可以用任何格式来保存日期。这时主要要注意的是,如果想进行日期值的比较,我们首先要把日期的字符串形式转化为DATETIME形式。
下面的内容包含一些测试问题和实战练习。这些测试问题的目的在于检验对学习内容的理解程度。实战练习有助于把学习的内容应用于实践,并且巩固对知识的掌握。在继续学习之前请先完成测试与练习,答案请见附录C。
1.系统日期和时间源自于哪里?
2.列出DATETIME值的标准内部元素。
3.如果公司是个国际公司,在处理日期和时间的比较与表示时,应该考虑的一个重要因素是什么?
4.字符串表示的日期值能不能与定义为某种DATETIME类型的日期值进行比较?
5.在SQL Server、MySQL和Oracle里,使用什么函数获取当前日期和时间?
1.在不同系统中输入以下SQL代码,从服务器显示当前日期:

2.输入以下SQL代码,显示每名雇员的受雇日期:

3.在MySQL里,通过联合使用EXTRACT函数与MySQL日期描述,我们能够以多种格式显示日期。输入以下代码显示每名雇员的受雇年份:

4.在Microsoft SQL Server中运行以下代码:

5.输入以下类似MySQL实现的代码,显示当前日期和每名雇员的受雇日期:

6.每名雇员是在星期几被雇用的?
7.今天的儒略日期(积日)是多少?
8.输入3行SQL代码。第1行获得系统时间(参考练习1),第2行将系统时间转换成日期型数据,第3行将系统时间转换成时间值。
