2.1  SELECT 语句
SQL 语句是由简单的英语单词构成的。这些单词称为
关键字,每个 SQL语句都是由一个或多个关键字构成的。最经常使用的
SQL 语句大概就是 SELECT 语句了。它的用途是从一个或多个表中检索
信息。

关键字(keyword)
作为 SQL组成部分的保留字。关键字不能用作表或列的名字。附录 D
列出了某些经常使用的保留字。

为了使用 SELECT 检索表数据,必须至少给出两条信息——想选择什么,
以及从什么地方选择。 

说明:理解例子
本书各课程中的样例 SQL语句(和样例输出)使用了附录 A中描述的
一组数据文件。如果想要理解和试验这些样例(我强烈建议这样做),
请参阅附录 A,它解释了如何下载或创建这些数据文件。

提示:使用正确的数据库
利用 DBMS 可以处理多个数据库(参见第 1 课里文件柜的比喻)。根
据附录 A安装好样例表之后,建议你把它们装进新的数据库。如果这
样的话,要确保在处理之前就选择好了数据库,就像你在创建样例表
之前做的那样。后面各课的学习过程中,如果你遇到未知表的错误,
很可能就是没在正确的数据库里。 

2.2 检索单个列
我们将从简单的 SQL  SELECT 语句讲起,此语句如下所示:
输入▼

SELECT prod_name
FROM Products;

 分析▼
上述语句利用 SELECT 语句从 Products 表中检索一个名为 prod_name
的列。所需的列名写在 SELECT 关键字之后, FROM 关键字指出从哪个表
中检索数据。此语句的输出如下所示:

输出▼

prod_name
-------------------
Fish bean bag toy
Bird bean bag toy
Rabbit bean bag toy
8 inch teddy bear
12 inch teddy bear
18 inch teddy bear
Raggedy Ann
King doll
Queen doll

根据你使用的具体 DBMS和客户端,可能你会看到一条信息说明检索了
多少行,以及花了多长时间。例如,MySQL命令行会显示类似下面这样
的一行信息:
9 rows in set (0.01 sec)

说明:未排序数据
如果你自己试验这个查询,可能会发现显示输出的数据顺序与这里的
不同。出现这种情况很正常。如果没有明确排序查询结果(下一课介
绍怎样指定顺序),则返回的数据没有特定的顺序。返回数据的顺序可
能是数据被添加到表中的顺序,也可能不是。只要返回相同数目的行,
就是正常的。

如上的一条简单 SELECT 语句将返回表中的所有行。数据没有过滤(过
滤将得出结果集的一个子集),也没有排序。以后几课将讨论这些内容。

提示:结束 SQL 语句
多条 SQL语句必须以分号( ; )分隔。多数 DBMS 不需要在单条 SQL
语句后加分号,但也有 DBMS可能必须在单条 SQL语句后加上分号。
当然,如果愿意可以总是加上分号。事实上,即使不一定需要,加上
分号也肯定没有坏处。 

 提示:SQL 语句和大小写
请注意,SQL语句不区分大小写,因此 SELECT 与 select 是相同的。
同样,写成 Select 也没有关系。许多 SQL开发人员喜欢对 SQL关键
字使用大写,而对列名和表名使用小写,这样做代码更易于阅读和调
试。不过,一定要认识到虽然 SQL是不区分大小写的,但是表名、列
名和值可能有所不同(这有赖于具体的 DBMS及其如何配置)。

提示:使用空格
在处理 SQL语句时,其中所有空格都被忽略。SQL语句可以写成长长
的一行,也可以分写在多行。下面这 3种写法的作用是一样的。

SELECT prod_name
 FROM Products;
 SELECT prod_name FROM Products;
 SELECT
 prod_name
 FROM
 Products;


多数 SQL开发人员认为,将 SQL语句分成多行更容易阅读和调试 

 2.3 检索多个列
要想从一个表中检索多个列,仍然使用相同的 SELECT 语句。唯一的不
同是必须在 SELECT 关键字后给出多个列名,列名之间必须以逗号分隔。
提示:当心逗号
在选择多个列时,一定要在列名之间加上逗号,但最后一个列名后不
加。如果在最后一个列名后加了逗号,将出现错误。
下面的 SELECT 语句从 Products 表中选择 3列。
输入▼

SELECT prod_id, prod_name, prod_price
FROM Products;

分析▼
与前一个例子一样,这条语句使用 SELECT 语句从表 Products 中选择数据。在这个例子中,指定了 3 个列名,列名之间用逗号分隔。此语句的
输出如下:
输出▼

prod_id prod_name prod_price
--------- -------------------- ----------
BNBG01 Fish bean bag toy 3.49
BNBG02 Bird bean bag toy 3.49
BNBG03 Rabbit bean bag toy 3.49
BR01 8 inch teddy bear 5.99
BR02 12 inch teddy bear 8.99
BR03 18 inch teddy bear 11.99
RGAN01 Raggedy Ann 4.99
RYL01 King doll 9.49
RYL02 Queen dool 9.49

说明:数据表示
SQL语句一般返回原始的、无格式的数据,不同的 DBMS和客户端显
示数据的方式略有不同(如对齐格式不同、小数位数不同)。数据的格
式化是表示问题,而不是检索问题。因此,如何表示一般会在显示该
数据的应用程序中规定。通常很少直接使用实际检索出的数据(没有
应用程序提供的格式)。

2.4 检索所有列
除了指定所需的列外(如上所述,一列或多列), SELECT 语句还可以检
索所有的列而不必逐个列出它们。在实际列名的位置使用星号( * )通配
符可以做到这点,如下所示。
输入▼

SELECT *
FROM Products;

 分析▼
如果给定一个通配符( * ),则返回表中所有列。列的顺序一般是表中出
现的物理顺序,但并不总是如此。不过,SQL数据很少直接显示(通常,
数据返回给应用程序,根据需要进行格式化,再表示出来)。因此,这不
应该造成什么问题。

注意:使用通配符
一般而言,除非你确实需要表中的每一列,否则最好别使用 * 通配符。
虽然使用通配符能让你自己省事,不用明确列出所需列,但检索不需
要的列通常会降低检索速度和应用程序的性能。

提示:检索未知列
使用通配符有一个大优点。由于不明确指定列名(因为星号检索每一
列),所以能检索出名字未知的列 

2.5 检索不同的值
如前所述, SELECT 语句返回所有匹配的行。但是,如果你不希望每个值
每次都出现,该怎么办呢?例如,你想检索 Products 表中所有产品供
应商的 ID:
输入▼

SELECT vend_id
FROM Products;

 输出▼

vend_id
----------
BRS01
BRS01
BRS01
DLL01
DLL01
DLL01
DLL01
FNG01
FNG01

 SELECT 语句返回 9行(即使表中只有 3个产品供应商),因为 Products
表中有 9种产品。那么如何检索出不同的值?
办法就是使用 DISTINCT 关键字,顾名思义,它指示数据库只返回不同
的值。
输入▼

SELECT DISTINCT vend_id
FROM Products;

分析▼
SELECT DISTINCT vend_id 告诉 DBMS 只返回不同(具有唯一性)的
vend_id 行,所以正如下面的输出,只有 3 行。如果使用 DISTINCT 关
键字,它必须直接放在列名的前面。

输出▼

vend_id
----------
BRS01
DLL01
FNG01

注意:不能部分使用 DISTINCT
DISTINCT 关键字作用于所有的列,不仅仅是跟在其后的那一列。例
如,你指定 SELECT DISTINCT vend_id, prod_price ,则 9行里的
6 行都会被检索出来,因为指定的两列组合起来有 6 个不同的结果。
若想看看究竟有什么不同,你可以试一下这样两条语句:
SELECT DISTINCT vend_id, prod_price FROM Products;
SELECT vend_id, prod_price FROM Products;

2.6 限制结果
SELECT 语句返回指定表中所有匹配的行,很可能是每一行。如果你只想
返回第一行或者一定数量的行,该怎么办呢?这是可行的,然而遗憾的
是,各种数据库中的这一 SQL实现并不相同。
在 SQL Server 中使用 SELECT 时,可以用 TOP 关键字来限制最多返回多
少行,如下所示:
输入▼

SELECT TOP 5 prod_name
FROM Products;

 输出▼

prod_name
-----------------
8 inch teddy bear
12 inch teddy bear
18 inch teddy bear
Fish bean bag toy
Bird bean bag toy

分析▼
上面代码使用 SELECT TOP 5 语句,只检索前 5行数据。
如果你使用的是 DB2,就得使用下面这样的 DB2特有的 SQL语句:

输入▼

SELECT prod_name
FROM Products
FETCH FIRST 5 ROWS ONLY;

分析▼
FETCH FIRST 5 ROWS ONLY 就会按字面的意思去做的(只取前 5行)。
如果你使用 Oracle,需要基于 ROWNUM (行计数器)来计算行,像这样:
输入▼

SELECT prod_name
FROM Products
WHERE ROWNUM <=5;

如果你使用 MySQL、MariaDB、PostgreSQL或者 SQLite,需要使用 LIMIT
子句,像这样:
输入▼

SELECT prod_name
FROM Products
LIMIT 5;

分析▼
上述代码使用 SELECT 语句来检索单独的一列数据。 LIMIT 5 指示MySQL等 DBMS返回不超过 5行的数据。这个语句的输出参见下面的代码。
为了得到后面的 5 行数据,需要指定从哪儿开始以及检索的行数,像
这样:
输入▼

SELECT prod_name
FROM Products
LIMIT 5 OFFSET 5;

分析▼
LIMIT 5 OFFSET 5 指示 MySQL等 DBMS返回从第 5行起的 5行数据。
第一个数字是检索的行数,第二个数字是指从哪儿开始。这个语句的输
出是:

输出▼

prod_name
-------------------
Rabbit bean bag toy
Raggedy Ann
King doll
Queen doll

所以, LIMIT 指定返回的行数。 LIMIT 带的 OFFSET 指定从哪儿开始。
在我们的例子中, Products 表中只有 9种产品,所以 LIMIT 5 OFFSET
5 只返回了 4行数据(因为没有第 5行)。

注意:第 0 行
第一个被检索的行是第 0行,而不是第 1行。因此, LIMIT 1 OFFSET
1 会检索第 2行,而不是第 1行。

提示:MySQL、MariaDB 和 SQLite 捷径
MySQL、MariaDB和 SQLite可以把 LIMIT 4 OFFSET 3 语句简化为 LIMIT
3,4 。使用这个语法,逗号之前的值对应 OFFSET ,逗号之后的值对应
LIMIT (反着的,要小心)。 

说明:并非所有的 SQL 实现都一样
我加入这一节只有一个原因,就是要说明,SQL虽然通常都有相当一
致的实现,但你不能想当然地认为它总是这样。非常基本的语句往往
是相通的,但较复杂的语句就不同了。当你针对某个问题寻找 SQL解
决方案时,一定要记住这一点。 

2.7 使用注释
可以看到,SQL语句是由 DBMS处理的指令。如果你希望包括不进行处理
和执行的文本,该怎么办呢?为什么你想要这么做呢?原因有以下几点。
  我们这里使用的 SQL 语句都很短,也很简单。然而,随着 SQL 语句
变长,复杂性增加,你就会想添加一些描述性的注释,这便于你自己
今后参考,或者供项目后续参与人员参考。这些注释需要嵌入在 SQL
脚本中,但显然不能进行实际的 DBMS 处理。(相关示例可以参见附
录 B中使用的 create.sql和 populate.sql。)
  这同样适用于 SQL文件开始处的内容,它可能包含程序描述以及一些
说明,甚至是程序员的联系方式。(相关示例也可参见附录 B 中的那
些.sql文件。)
  注释的另一个重要应用是暂停要执行的 SQL代码。如果你碰到一个长
SQL语句,而只想测试它的一部分,那么应该注释掉一些代码,以便
DBMS略去这些注释。

很多 DBMS都支持各种形式的注释语法。我们先来看行内注释:
输入▼

SELECT prod_name -- 这是一条注释
FROM Products;

 分析▼
注释使用 -- (两个连字符)嵌在行内。 -- 之后的文本就是注释,例如,
这用来描述 CREATE TABLE 语句中的列就很不错。
下面是另一种形式的行内注释(但这种形式有些 DBMS不支持 )。
输入▼

# 这是一条注释
SELECT prod_name
FROM Products;

分析▼
在一行的开始处使用 # ,这一整行都将作为注释。你在本书提供的脚本
create.sql和 populate.sql中可以看到这种形式的注释。
你也可以进行多行注释,注释可以在脚本的任何位置停止和开始。
输入▼

/* SELECT prod_name, vend_id
FROM Products; */
SELECT prod_name
FROM Products;

分析▼
注释从 /* 开始,到 */ 结束, /* 和 */ 之间的任何内容都是注释。这种方式
常用于把代码注释掉,就如这个例子演示的,这里定义了两个 SELECT
语句,但是第一个不会执行,因为它已经被注释掉了。