SQL新手教程

来自KlniuWiki
(重定向自SQL
跳转到: 导航, 搜索

SQL是结构化查询语言(Structured Query Language)的缩写。这种语言允许我们对数据库进行复杂的查询。同时也提供了创建数据库的方法。SQL语言的使用范围非常广泛。许多数据库产品都支持SQL语言,这意味着如果我们学会了SQL语言,我们可以把这种知识运用到MS Access 或 SQL Server, Oracle, DB2以及非常多的其它数据库中。

SQL语言运用在关系型数据库中。一个关系型数据库把数据存储在表(也称关系)中。每个数据库的主要组成就是一组表。每个表又由一组记录组成--每条记录在表中有相同的结构,包含固定数量的具有一定类型的字段。

目录

1 术语

数据库(Database)
存储有一定组织数据的一个容器(通常是一个或者一组文件)。
表(Table)
存储特殊数据类型的一个结构化列表。
表名(Table Names)
数据库中的每一个表都有一个唯一的名字用于标识。
视图(Schema)
关于数据库的信息以及表的布局与性质。
列(Column)
所有的表是由一列或者多列组成的,例如将表划分为网格的形式,在网格中的每一列都包含一段特殊的信息。
数据类型(DataType)
被允许输入的数据类型,表格中的每一列都被限制只能输入特殊的数据类型,比如整型、字符串、文本等。
行(Rows)
表中的一条记录视作一行,例如将表划分为网格的形式,在网格中的每一行都包含一系列的信息,比如一个人的姓名、年龄、职业等等可以视作一条记录或者一行,多行则表示多个人的记录。
主键(Primary Keys)
表中的一列或者几列,其中的值可以输入唯一的值,用以和其他行区别,这个主键可以用来标识行的不同,比如ID,编号等等。
子句(Clause)
SQL语句是由子句组成的,有些是必须的,有些是可选的,一个子句一般是由关键字和需要进行操作的数据组成的。
空值(NULL)
表示没有值,即不等于0,也不是空字符串或者空格。

2 Note

  • 本文使用的例子大部分来源于 Sams Teach Yourself SQL in 10 Minutes, Third Edition,可以查看参见,例表可以从 http://www.forta.com/books/0672325675/ 下载,并执行其中的语句。
  • SQL会忽略空白字符,因此,你可以在编写语句的时候换行,以方便阅读与调试。
  • 当存在多条SQl语句时,必须在每条语句后添加一个分号(;),一些客户端在执行单个SQL语句的时候也需要在语句结尾添加分号(;),当然,有些并不需要,如果你不管客户端是否要求,均在SQL语句后面添加(;)是没有错的。
  • SQL的关键字是不区分大小写的,例如,SELECT和select是一样的,当然,大家都习惯使用大写来表示SQL关键字,便于阅读与调试,但切记,数据库内容,比如表名,字段都是区分大小写的。

3 查询数据

3.1 查询单列数据

SELECT prod_name
FROM Products;

上面的语句表示从表 Products 中查询列名为 prod_name 的所有记录。SELECT 后跟要查询的列名,FROM 后跟表的名称,表示要在哪个表进行查询。

3.2 查询多列数据

SELECT prod_id, prod_name, prod_price
FROM Products;

上面的语句表示从表 Products 中查询列名分别为 prod_id、prod_name、prod_price 的数据,和单列数据不同的是,在查询多列时,列名之间要用逗号(,)分开。

3.3 查询所有列数据

SELECT *
FROM Products;

上面的语句表示从表 products 中查询所有列的记录。*为通配符,表示全部列。

Note: 在实际使用的过程中,如果不要需要查询所有表的情况下,不建议使用通配符,因为它为增加查询的时间,当然,如果你在不知道列名的情况下,也是可以使用*通配符来查询的。

4 数据排序

4.1 数据排序

SELECT prod_name
FROM Products;

上面的数据在返回结果时并不是排过序的,它经常会出现不同的顺序。如果要对 SELECT 查询的数据进行排序,则需要使用 ORDER BY 子句,并依据一列或者多列进行排序。例如:

SELECT prod_name
FROM Products
ORDER BY prod_name;

上句表示从 Products 中查询 prod_name 列,并依据 prod_name 进行排序,当使用 ORDER BY 子句进行排序时,此子句要放在 SELECT 语句的最后,

4.2 多列数据排序

有时候需要对多列数据进行排序,例如:

SELECT prod_id, prod_price, prod_name
FROM Products
ORDER BY prod_price, prod_name;

上面的语句表示从表 Products 中查询列名 prod_id、prod_price、prod_name 的数据,并优先按照 prod_price 排序,重复项再按照 prod_name 排序。

4.3 从列的索引位置对数据排序

排序除了使用列的名字外,还可以使用列在表中的相对位置进行排序,例如:

SELECT prod_id, prod_price, prod_name
FROM Products
ORDER BY 2, 3;

上面排序是按照第2与第3的列表进行排序的。而prod_price和prod_name就正好是表中的第2列与第3列。

4.4 指定排序方向

默认的排序都是按照升序排列的,比如从A-Z,不过可以指定 DESC 关键字降序排列。例如:

SELECT prod_id, prod_price, prod_name
FROM Products
ORDER BY prod_price DESC;

DESC 关键字指定的列进行降序排列,如果进行多列排序的时,可以为每列指定排序的方式,例如:

SELECT prod_id, prod_price, prod_name
FROM Products
ORDER BY prod_price DESC, prod_id, prod_name DESC;

上面语句prod_price、prod_name 列进行降序排序,prod_id 按照默认的升序排序。

5 筛选数据

5.1 筛选数据(初步)

数据库通常都包含大量的数据,用户很少会需要全部的数据的,因此就要对数据进行筛选,下面将介绍 SELECT 语句 WHERE 子句的筛选功能。

在 SELECT 语句中,可以通过 WHERE 子句对数据进行筛选,WHERE 子句一般放在 FROM 关键字的右边。例如:

SELECT prod_name, prod_price
FROM Products
WHERE prod_price = 3.49;

上面表示显示表 Products 中 prod_price 列数值为 3.49 的记录,且只显示 prod_name 和 prod_price 列。

Note: 在 Picky PostgreSQL 数据库程序中,PostgreSQL 对传入的数据要求非常严格,因此上例需要将 = 3.49 替换为 = decimal '3.49'。

WHERE 子句不仅可以进行是否相等的比较,还有以下比较的方式,下表列出的一些操作符有重复的,而且不是所有的数据库管理系统对这些都支持,具体请参看系统的手册:

Operator Description
= 相等
<> 不等
!= 不等
< 小于
<= 小于等于
!< 不小于
> 大于
>= 大于等于
!> 不大于
BETWEEN 在两值之间
IS NULL 空值

下面再给出几个例子:

查询所有小于10的记录

SELECT prod_name, prod_price
FROM Products
WHERE prod_price < 10;

查询不符合的记录

SELECT vend_id, prod_name
FROM Products
WHERE vend_id <> 'DLL01';

对于非数字类型,比如文本,字符串,需要使用单引号,但数值类型的值就不能使用单引号了。

查询一个范围的数据

SELECT prod_name, prod_price
FROM Products
WHERE prod_price BETWEEN 5 AND 10;

BETWEEN 关键字后面必须跟两个数值,表示一个范围,且两数值中间以一个 AND 连接,且此范围包括指定的这两个数值。

查询为空值的记录

SELECT prod_name
FROM Products
WHERE prod_price IS NULL;

5.2 筛选数据(高级)

上述的筛选数据功能一次只使用一个条件进行筛选,如果要使用多级筛选功能,就要使用 AND 和 OR 操作符将条件进行连接,执行多级筛选。

5.2.1 使用 AND 操作符

AND 表示与功能,意味着筛选出的数据必须满足所有的条件。

SELECT prod_id, prod_price, prod_name
FROM Products
WHERE vend_id = 'DLL01' AND prod_price <= 4;

上面表示筛选出的数据必须同时满足 vend_id 为 DLL01 和 prod_price 小于等于 4 两个条件。

5.2.2 使用 OR 操作符

OR 表示或功能,意味着数据只需要满足OR连接的条件中的一个或者多个即可。

SELECT prod_name, prod_price
FROM Products
WHERE vend_id = 'DLL01' OR vend_id = 'BRS01';

上面表示筛选出的数据只要满足 vend_id 为 DLL01 和 prod_price 小于等于 4 中的一个或两个条件即可。

5.2.3 表达式的顺序

WHERE 子句后面可以跟任意多个的 AND 和 OR 操作符,连接这些操作符就可以执行高级和复杂的筛选功能。

SELECT prod_name, prod_price
FROM Products
WHERE (vend_id = 'DLL01' OR vend_id = 'BRS01')
    AND prod_price >= 10;

上句表示筛选出,vend_id 为 DLL01 或者 BRS01,并且 prod_price 小于等于 10 的数据,但如果将上句的括号去掉,出现的结果就会出现问题,因为 AND 的优先级比 OR 高,所以 vend_id = 'BRS01 就会和 prod_price >= 10 先结合,之后再和 vend_id = 'DLL01' 结合。

Tip: 建议在使用多个条件时,将表达式用括号括起来,这样便于阅读,且不容易出错

5.2.4 使用 IN 操作符

IN 操作符用来指定一系列条件的范围,其中的任一条件都可以被匹配,IN 后跟一个用括号包起来的一系列值,并用逗号(,)分隔。

SELECT prod_name, prod_price
FROM Products
WHERE vend_id  IN ('DLL01','BRS01')
ORDER BY prod_name;

上句表示将筛选出所有 vend_id 为 'DLL01' 或 'BRS01' 的数据,并按照 prod_name 进行排序。

IN 操作符的作用就类似于 OR,不过它还有以下优点:

  • 当使用一个多条件的列表时,IN 更加简单,易于阅读。
  • 使用IN操作符,不用担心操作符的优先级问题。
  • IN 的执行速度比 OR 快。
  • 最大的好处是,IN 后跟的条件列表中可以使用 SELECT 语句。

5.2.5 使用 NOT操作符

NOT 操作符的作用是对条件取反,且不能单独使用,只能后跟条件。

SELECT prod_name
FROM Products
WHERE NOT vend_id  = 'DLL01'
ORDER BY prod_name;

上句将取 vend_id 不为 'DLL01'的数据。也就是说 NOT vend_id = 'DLL01' 也可表示为 vend_id <> 'DLL01'。

Note: 在MySQL中,NOT 操作符只能和 EXIST 连接,表示是否存在,不能表示像上述一样应用。

6 使用通配符

通配符的作用是能够代替那些不知道或者数量比较大的列或者字段。当要使用通配符是,需要在包含通配符的字符串的前面加上 LIKE 断言,以告诉数据库系统后面需要匹配的是一个包含通配符的字符串。

Note: 通配符只能应用于字符串与文本,不能用于非字符串型数据类型。

通配符很强大,但需要注意以下几点:

  • 不要过度使用通配符,如果有一个检索操作能够代替通配符的话,就尽量使用没有通配符的操作。
  • 当使用通配符时,除非非常需要,不要在检索模式的开始就使用它,因为通配符检索会减缓操作的速度。
  • 注意使用通配符来替换字符,如果替换的位置错误,将得不到正确的结果。

6.1 百分号(%)通配符

百分号(%)可以匹配任意数量的任意字符,包括0个。例如,下面语句将匹配 prod_name 中所有以'Fish'开头的字符串。

SELECT prod_id, prod_name
FROM Products
WHERE prod_name LIKE 'Fish%';
Note: 如果你使用 Microsoft Access,你可能需要使用星号(*)来代替百分号(%)。

百分号(%)可以使用多个以及多个位置,比如 '%Fish%'、'F%sh'、'F%s%'。

Note: 在许多DBMS中,包括 Microsoft Access,假如一个字段是默认设定宽度为31字符,而实际字符只有17个字符,那么,系统就会在字符串尾加上空格以补全31个字符,于是,在使用通配符的时候,F%sh并不能匹配正确,为此,解决办法之一是使用F%sh%,另一种办法是使用函数功能来修整空格。

6.2 下划线(_)通配符

下划线(_)匹配任意的单个字符。

Note: 如果你使用 Microsoft Access,你可能需要使用问号(?)来代替下划线(_)。
SELECT prod_id, prod_name
FROM Products
WHERE prod_name LIKE '% inch teddy bear';

6.3 中括号([])通配符

中括号([])用来匹配一组字符中的一个。例如,以下语句匹配 cust_contact 列中,以 J 或 M 开头的字符串。

SELECT cust_contact
FROM Customers
WHERE cust_contact LIKE '[JM]%'
ORDER BY cust_contact;
Note: 不是所有的 DBMS 均支持中括号通配符,支持中括号的有 Microsoft Access、Microsoft SQL Server、Sybase Adaptive Server。

中括号([])通配符还可以使用^来取反,比如[^JM]是匹配不为J或M的任意单个字符。

Note: 如果你使用 Microsoft Access,你可能需要使用 ! 来代替 ^。

当然,仍然可以使用NOT操作符来执行相反操作,不过^更加简洁,例如:

SELECT cust_contact
FROM Customers
WHERE NOT cust_contact LIKE '[JM]%'
ORDER BY cust_contact;

7 数据处理

当需要对数据库内的数据进行处理或者计算然后再返回数据,就要使用区域计算。

7.1 连接两列数据

连接数据可以使用+或者||操作符,Access、SQL Server和 Sybase support支持+操作符,DB2、Oracle、PostgreSQL 和 Sybase support 支持 ||。不过 || 开始被越来越多DBMS支持了。

例如,下面语句在返回的结果中,将 vend_country 用括号包起来,并和 vend_name 连接。

下面例子被大多数 DBMS 支持:

SELECT vend_name + ' (' + vend_country + ')'
FROM Vendors
ORDER BY vend_name;

下面的例子使用 ||:

SELECT vend_name || ' (' || vend_country || ')'
FROM Vendors
ORDER BY vend_name;

返回结果:

----------------------------------------------------
Bear Emporium                     (USA             )

Bears R Us                        (USA             )

Doll House Inc.                   (USA             )

Fun and Games                     (England         )

Furball Inc.                      (USA             )

Jouets et ours                    (France          )

在MySQL中,+和||都不被支持,如果要在MySQL中使用连接功能,需要使用函数CONCAT(),此函数将一系列的参数连接起来,例如:

SELECT CONCAT(vend_name, ' (', vend_country, ')')
FROM Vendors
ORDER BY vend_name;

上述的返回结果右边出现一大片的空格,这是因为许多数据库都会为长度不满足默认长度的数值在结尾填充空格,处理空格需要使用 RTRIM()函数,该函数去除返回结果中右边的所有空格,例如:

SELECT RTRIM(vend_name) + ' (' + RTRIM(vend_country) + ')'
FROM Vendors
ORDER BY vend_name;

返回:

----------------------------------------------------

Bear Emporium (USA)

Bears R Us (USA)

Doll House Inc. (USA)

Fun and Games (England)

Furball Inc. (USA)

Jouets et ours (France)

The following is the same statement, but using the || syntax:

与 RTRIM() 类似,LTRIM() 去除左边的空格,TRIM() 去除两边的空格。

7.2 使用别名

上述处理过的结果,并没有列名,因此它只能用于显示,却不能被引用,如果客户端想要引用的话,就必须给结果列进行命名,这就是别名的作用,可以给返回的结果列添加一个名称,用于引用,别名需要在 AS 关键字之后指定,例如:

SELECT vend_name + ' (' + vend_country + ')' AS vend_title
FROM Vendors
ORDER BY vend_name;

下面的例子使用 ||:

SELECT vend_name || ' (' || vend_country || ')' AS vend_title
FROM Vendors
ORDER BY vend_name;

返回:

vend_title

----------------------------------------------------

Bear Emporium (USA)

Bears R Us (USA)

Doll House Inc. (USA)

Fun and Games (England)

Furball Inc. (USA)

Jouets et ours (France)

7.3 数学计算

下面例子将 quantity 和 item_price 相乘的结果添加别名 expanded_price 后返回:

SELECT prod_id,
       quantity,
       item_price,
       quantity*item_price AS expanded_price
FROM OrderItems
WHERE order_num = 20008;

SQL支持基本的数学运算,如加(+)、减(-)、乘(*)、除(/)。

8 使用函数

SQL中函数的作用是使数据转换和操作更加容易,前面讲到的 RTRIM() 就是一个函数。

Warning: 需要注意的是,只有很少的函数是所有的 DBMS 均支持的,DBMS 都有自己的一套函数,因此,在写SQL的时候,你要考虑是否使用函数,并且做好注释,以防止更换系统时麻烦。

大部分 DBMS 都只支持以下类型的函数:

  • 文本函数,习惯被用来操作文本或字符串,例如剪切或者填充,更改大小写等。
  • 数值函数,习惯被用来操作数值,例如返回绝对值、代数运算。
  • 日期与时间函数,习惯被用来操作日期与时间值,以及从特殊的组成中提取出日期与时间,例如,返回两个日期的差、检查日期的有效性。
  • 系统函数,返回当前DBMS的信息,比如用户的登陆信息等。

8.1 文本函数

下面的 UPPER() 函数的功能是转换结果为大写。

SELECT vend_name, UPPER(vend_name)
AS vend_name_upcase
FROM Vendors
ORDER BY vend_name;

下表列出经常使用的文本处理函数:

经常使用的文件处理函数
函数 函数描述
LEFT() (或者使用 substring 函数) 从左边开始返回字符
LENGTH() (也可以使用 DATALENGTH() or LEN()) 返回字符串的长度
LOWER() 将字符串的大写字符转换为小写
LTRIM() (在 Access 中为LCASE()) 删除左边空白字符
RIGHT() (或者使用 substring 函数) 从右边开始返回字符
RTRIM() 删除右边空白字符
SOUNDEX() 返回一个字符串的语音表示形式。
UPPER() (在 Access 中为 UCASE()) 将字符串中的小写字符转换为大写

8.2 日期和时间函数

日期和时间在表中以数据类型的形式存储,而且每个 DBMS 都使用它们自己特殊的形式,它们存储的形式比较特殊,以方便排序、查找和存储。

时间和日期函数在不同的平台下,差别比较大,请查看具体手册。

8.3 数学函数

数学函数大部分是对数值进行代数、三角、几何计算的,很多平台的数值函数都是相同的,下面列出一些常用的数学函数。

常用的数值函数
函数 描述
ABS() 返回数值的绝对值
COS() 返回余弦值
EXP() 返回针对一个特定数字的指数值
PI() 返回圆周率的值
SIN() 返回正弦值
SQRT() 返回数值的平方根
TAN() 返回数值的正弦值

8.4 统计函数

在 SQL 中,统计函数的功能是对特定列计数、计和、最值等统计作用。下表列出各类统计函数:

SQL 统计函数
函数 描述
AVG() 返回一列的平均值
COUNT() 返回行数
MAX() 返回一列中的最大值
MIN() 返回一列中的最小值
SUM() 返回一列数据的总和

所有的统计类函数都可以应该于多列。


8.4.1 AVG()平均值

AVG()平均值函数用来返回特定一列的特定行的数值的平均值。例如,下例返回 prod_price 列全部行的平均值。:

SELECT AVG(prod_price) AS avg_price
FROM Products;

当然,可以在后面加入条件限制,以针对特定行执行平均值操作。

8.4.2 COUNT()计数

计数函数是统计行数的工具,它有两个实现的方式:

  • COUNT(*),将对所有行进行计数,不管各列是否有值。
  • COUNT(column),将对特定列内有值的行进行计数,忽略空值的行。

8.4.3 MAX()最大值

MAX()返回特定列的最大值,该函数需要一个特定列作为参数,但尽量不要将该函数应用于非数值列。例如,下例返回 prod_price 列的最大值:

SELECT MAX(prod_price) AS max_price
FROM Products;

8.4.4 MIN()最小值

MIN()返回特定列的最小值,用法于MAX()相同。

8.4.5 SUM()计和

SUM()函数返回特定列并符合条件的值的总和,该函数将忽略包含空值的项目。例如,下例返回满足 order_num 等于20005的 quantity 列的所有值的总和。

SELECT SUM(quantity) AS items_ordered
FROM OrderItems
WHERE order_num = 20005;

SUM()函数还可以对计算过的数值进行计和。例如,下例将返回各行 item_price 和 quantity 相乘之后相加的和。

SELECT SUM(item_price*quantity) AS total_price
FROM OrderItems
WHERE order_num = 20005;

8.4.6 统计个别数据

统计类函数有两种使用方式:

  • 一种是作用于全部满足条件的值。
  • 一种是作用于非重复数据的值,也就是说该列中只有此一个值。如果要这样统计的话,就需要使用 DISTINCT 关键字。
SELECT AVG(DISTINCT prod_price) AS avg_price
FROM Products
WHERE vendor_id = 'DLL01';

DISTINCT 不能应该用于COUNT(*)函数,因为 DISTINCT 要应用于列名之前。同时,DISTINCT 用在 MIN() 和 MAX() 上的,因为它没有意义。

8.4.7 合并多个函数

上面的函数例子都是使用单个函数的,实际上,多个函数可以并用,比如:

SELECT COUNT(*) AS num_items,
       MIN(prod_price) AS price_min,
       MAX(prod_price) AS price_max,
       AVG(prod_price) AS price_avg
FROM Products;

9 分组查询

9.1 创建分组

分组查询的作用是将一列中相同的值分为一组,并统计各组情况,例如,下例的返回有两列,一个是 vend_id,一个是 num_prods,此列是要被统计数量的列,GROUP BY 关键字将按照 vend_id 列进行分组和排序,同时 COUNT() 函数将对每一组的数值统计一次,而不是返回整个数组的统计值:

SELECT vend_id, COUNT(*) AS num_prods
FROM Products
GROUP BY vend_id;

返回:

vend_id    num_prods

---------    ---------

BRS01    3

DLL01    4

FNG01    2

在使用 GROUP BY 之前,需要注意以下规则:

  • GROUP BY 子句之后可以跟多个列,这个特性可以使你对组进行嵌套,以及控制怎么分组。
  • 如果你嵌套使用 GROUP BY 子句,数据只会在最后一组被统计。
  • 在 GROUP BY 子句中列出的每一列都应该是可以被检索的或者是一个有效的表达式。如果一个表达式在 SELECT 子句中使用了,那么 GROUP BY 子句中也应该包含它。
  • 大部分的 DBMS 都不允许 GROUP BY 后跟可变长度的数据类型,比如文本。
  • 除了函数外,在 SELECT 语句中出现的列都应该出现在 GROUP BY 子句中。
  • 如果要分组的列内含有空值,那么返回的结果中会将是空值的分为一组,并且这一组也将被统计。
  • GROUP BY 子句必须是 WHERE 子句之后,ORDER BY 子句这前。

有些 DBMS 支持分组的时候,按照列的索引来分组,比如 GROUP BY 2,1,具体使用请查看手册。

9.2 过滤分组

WHERE 子句是对特定行进行过滤,但如果要过滤组就不能使用 WHERE 子句了,可以使用 HAVING 子句,HAVING 子句的用法和 WHERE 子句一样,例如,下例将返回所有 Orders 大于或等于2的组。

SELECT cust_id, COUNT(*) AS orders
FROM Orders
GROUP BY cust_id
HAVING COUNT(*) >= 2;

WHERE 子句和 HAVING 子句的区别是 WHERE 子句过滤行,也就是说是在 GROUP BY 子句这前执行,而 HAVING 子句过滤组,在 GROUP BY 子句之后执行。例如,下例将首先过滤掉 prod_price 小于4的行,分组之后,再过滤掉总计小于2的组:

SELECT vend_id, COUNT(*) AS num_prods
FROM Products
WHERE prod_price >= 4
GROUP BY vend_id
HAVING COUNT(*) >= 2;

9.3 分组与排序

分组返回的结果并不总是按照我们的意愿进行排序,或者有时候我们需要针对特定组进行排序,就要使用 ORDER BY 子句了,例如,下例分组完成后,再根据 COUNT(*) 和 order_num 进行先后排序:

SELECT order_num, COUNT(*) AS items
FROM OrderItems
GROUP BY order_num
HAVING COUNT(*) >= 3
ORDER BY items, order_num;
Note: Microsoft Access 不允许在排序的时候使用别名,解决办法是,不使用别名,直接在 ORDER BY 后跟 COUNT(*) 函数即可。

10 子句顺序

在学习了这么多 SELECT 子句后,需要知道各个子句的顺序是什么样的,见下表:

SELECT 子句及其顺序
子句 描述 是否必须
SELECT 返回列或者表达式
FROM 获取数据的来源表格 当从表格中获取数据时需要
WHERE 过滤行 不是
GROUP BY 分组 按照组统计时需要
HAVING 过滤组 不是
ORDER BY 对输出排序 不是

11 子查询

11.1 子查询

子查询允许你把查询的一个结果当作另一查询的一部分使用,即嵌套查询。例如,下例包含两个子查询,子查询的结果将返回后作为父查询的条件继续参与查询。

SELECT cust_name, cust_contact
FROM Customers
WHERE cust_id IN (SELECT cust_id
    FROM Orders
    WHERE order_num IN (SELECT order_num
        FROM OrderItems
        WHERE prod_id = 'RGAN01'));
Note: 子查询只能获取一列数据,获取多列数据系统会报告错误。

11.2 子查询作为计算子段

下例将对 Customers 中的每个客户返回三列,分别为 cust_name,cust_state,orders。其中的子句在每查到一个客户的时候都执行一次。子句中的 WHERE 关键字和主句中的不同,它使用了有限制的列名,因为 cust_id 在 Orders 和 Customers 中均有,且以此为联系进行查询,因此需要加限定。

SELECT cust_name,
cust_state,
(SELECT COUNT(*)
        FROM Orders
        WHERE Orders.cust_id = Customers.cust_id) AS
orders
FROM Customers
ORDER BY cust_name;

12 联结表

12.1 联结

SQL 最强大的功能之一就是能在数据查询的执行中联结表。联结是利用关系表来执行操作的,它不是物理意义上的联结,而是查询过程中的操作。应用联结需要知识关系表,关系表是指两个或者两个以上的表中,通过一个或者多个主键或者唯一值进行关联,达到互相关联的目的,比如,可以将产品的信息和生产厂家的信息分别存储在两个表内,两个表内相同的一列数据可以是生产厂商的ID,之后就可以很简单的通过产品查询到厂商,反之亦可。

12.2 创建联结

创建联结很简单,只需要指出所有的表及其它们之间的联系即可。例如:

SELECT vend_name, prod_name, prod_price
FROM Vendors, Products
WHERE Vendors.vend_id = Products.vend_id;

上例和以前的语句很相似,不同之处在于:

  • 要查询的列 prod_name,prod_price 位于一个表内,vend_name 位于另一个表内。
  • FROM 后跟两个表,表示从两个表内查询数据。
  • WHERE 子句后跟一个带有限定范围的表达式,出现限定符的原因是,因为有两个表,且其字段都为 vend_id,因此,需要指明方可。
Note: 切记在联结表的时候一定要使用 WHERE 子句并保证其正确。

12.3 内联结

目前为止所有的联结都是基于两个表的等值联结,这种联结也叫做内联结。另外有种语法可以查询到相同的结果,例如:

SELECT vend_name, prod_name, prod_price
FROM Vendors INNER JOIN Products
 ON Vendors.vend_id = Products.vend_id;

上句中的 SELECT 与前面的 SELECT 相同,但 FROM 子句不同,两个表的关系以 INNER JOIN 指定。在使用这种语法时,联结条件用特定的ON子句而不是WHERE子句给出。传递给ON的实际条件与传递给WHERE的相同。

12.4 联结多个表

SQL对一条SELECT语句中可以联结的表的数目没有限制。创建联结的基本规则也相同,例如:

SELECT prod_name, vend_name, prod_price, quantity
FROM OrderItems, Products, Vendors
WHERE Products.vend_id = Vendors.vend_id
       AND OrderItems.prod_id = Products.prod_id
       AND order_num = 20007;

DBMS在运行时关联指定的每个表以处理联结,这种处理可能非常耗费资源,因此应该仔细,不要联结不必要的表,联结的表越多,性能下降越厉害。

12.5 创建高级联结

12.5.1 使用表别名

前面已经介绍过列别名,可以方便在结果中引用,另外SQL也支持表别名,表别名有两个用途,一个是简化,另一个是方便后面引用。另外,与列别名不同的是,表别名并不返回到结果中去,只是在查询中使用。例如:

SELECT cust_name, cust_contact
FROM Customers AS C, Orders AS O, OrderItems AS OI
WHERE C.cust_id = O.cust_id
 AND OI.order_num = O.order_num
 AND prod_id = 'RGAN01';
Note: 在Oracle中,不支持使用AS来声明别名,请使用 Customers C 来代替 Customers AS C。

13 参见

  • 深入浅出SQL入门教程. sqlzoo.cn. 
  • Ben Forta. Sams Teach Yourself SQL in 10 Minutes, Third Edition. 2004-03-31.ISBN:0-672-32567-5.
个人工具
分类
化学
[×] 國學
学佛
[×] 数学
物理
生活
[×] 英语
读书
辞典
廣告