瑞星卡卡安全论坛技术交流区系统软件 【推荐】SQL 21 日自学通-1

1234   3  /  4  页   跳转

【推荐】SQL 21 日自学通-1

第五天SQL 中的子句
目标
今天的主题是子句— — 它不是你在渡假时的赠品而是你所学习的SELECT 语句的一
个组成部分在今天结束以后我们将学会以下子句
l WHERE
l STARTING WITH
l ORDER BY
l GROUP BY
l HAVING
为了对这些子句有一个大致的印象请看一下SELECT 语句的通用语法表达式
语法
SELECT [DISTINCT | ALL] { *
| { [schema.]{table | view | snapshot}.*
| expr } [ [AS] c_alias ]
[, { [schema.]{table | view | snapshot}.*
| expr } [ [AS] c_alias ] ] ... }
FROM [schema.]{table | view | snapshot}[@dblink] [t_alias]
[, [schema.]{table | view | snapshot}[@dblink] [t_alias] ] ...
[WHERE condition ]
[GROUP BY expr [, expr] ... [HAVING condition] ]
[{UNION | UNION ALL | INTERSECT | MINUS} SELECT command ]
[ORDER BY {expr|position} [ASC | DESC]
[ {expr|position} [ASC | DESC]] ...]
注根据我对SQL 的经验ANSI 标准确实只是ANSI 的建议上述的语法格式在
大多数的SQL 引擎下都能够工作但是你可以发现它们之间存在着一些差别
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 98
你无需对这些复杂的语法花费太多的精力因为许多人发现语法表比关于新应用的示
例更容易让人困惑本书采用简单的例子展现其特点不过如果我们在今天看一下有关
它的语法表述会使我们对今天的学习更容易理解
不要对语法的精确内容太担心对于不同的解释器它们是不同的所以我们应该把
精力放到关系上在最前边的是SELECT 语句我们在前几天已经多次使用过了SELECT
之后应该是FROM 在每次输入SELECT 语句时它也应该输入明天我们将学习FROM
语句的新用法之后是WHERE GROUP BY HAVING 和ORDER BY 其余的子句包
括UNION UNION ALL INTERSECT 和MINUS 在表中已经在第3 天时讲过了每一个
子句的在数据的选择和操作时都扮演着它的重要角色
注在今天的例子中我们使用两种SQL 解释器一种仍然是SQL 的命令行形式它
属于Personal Oracle7 而另外一种则不是它是BORLAND 公司的ISQL 你可以发现
输出的结果会随着解释器的不同而不同
WHERE 子句
仅使用SELECT 和FROM 子句你会受到表中的每一行数据均返回的限制例如只
在CHECKS 表中使用这两个关键字你将会得到表中的全部行共7 行
INPUT
SQL>SELECT * FROM CHECKS
OUTPUT
CHECK# PAYEE AMOUNT REMARKS
1 Ma Bell 150 Have sons next time
2 Reading R.R. 245.34 Train to Chicago
3 Ma Bell 200.32 Cellular Phone
4 Local Utilities 98 Gas
5 Joes Stale$ Dent 150 Groceries
16 Cash 25 Wild Night Out
17 Joans Gas 25.1 Gas
使用WHERE 子句将会使你更具有选择性要想找到你所填写的支票中所有超过100
元的你可以这样写
INPUT
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 99
SQL>SELECT * FROM CHECKS WHERE AMOUNT 100
WHERE 子句只返回了符合条件的四条记录
OUTPUT
CHECK# PAYEE AMOUNT REMARKS
1 Ma Bell 150 Have sons next time
2 Reading R.R 245.34 Train to Chicago
3 Ma Bell 200.32 Cellular Phone
5 Joes Stale $ Dent 150 Groceries
使用WHERE 也可以解决一些其它的难题下表给出了姓名和位置你可以提出这样
的问题— — Waldo 住在哪儿
INPUT
SQL>SELECT * FROM PUZZLE
OUTPUT
NAME LOCATION<br>TYLER BACKYARD
MAJOR KITCHEN
SPEEDY LIVING ROOM
WALDO GARAGE
LADDIE UTILITY CLOSET
ARNOLD TV ROOM
INPUT
SQL>SELECT LOCATION AS "WHERE'S WALDO?" FROM PUZZLE
WHERE NAME = 'WALDO'
OUTPUT
WHERE'S WALDO?
GARAGE
好了我答应以后不再写像这样粗俗的语句了我已经把它们收录于SQL BATHROOM
HUMOR — — 这是一本每个人都想得到的书不过这个查询显示出了在WHERE 中用
于条件的列并没有在SELECT 语句中出现本例中你所选择的是LOCATION 列而条件列
是NAME 这是完全合法的同时我们也应该注意到SELECT 语句中我们使用了AS
它是一个可以选择的参数用以指定LOCATION 的别名你以后将不会看到AS 因为它
是多余的ACCESS 中则不可省略— — 译者在大多数SQL 解释器中我们只需输入
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 100
INPUT
SQL>SELECT LOCATION "WHERE'S WALDO?" FROM PUZZLE WHERE
NAME='WALDO'
这里没有使用AS 但它的结果与上例是完全一样的
WHERE 是使用频度仅次于SELECT 和FROM 的语句
STARTING WITH 子句
STARTING WITH 子句附加于WHERE 子句上它的作用与LIKE exp% 相似试
比较下边的两个查询
INPUT
SELECT PAYEE AMOUNT REMARKS FROM CHECKS WHERE PAYEE
LIKE('Ca%')
OUTPUT
PAYEE AMOUNT REMARKS
Cash 25 Wild Night Out
Cash 60 Trip to Boston
Cash 34 Trip to Dayton
再看看下边的查询
INPUT
SELECT PAYEE AMOUNT REMARKS FROM CHECKS WHERE PAYEE STARTING
WITH('Ca')
OUTPUT
PAYEE AMOUNT REMARKS
Cash 25 Wild Night Out
Cash 60 Trip to Boston
Cash 34 Trip to Dayton
结果是相同的你甚至可以同时使用它们例如
INPUT
SELECT PAYEE AMOUNT REMARKS FROM CHECKS
WHERE PAYEE STARTING WITH('Ca') OR REMARKS LIKE 'G%'
OUTPUT
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 101
PAYEE AMOUNT REMARKS
Local Utilities 98 Gas
Joes Stale $ Dent 150 Groceries
Cash 25 Wild Night Out
Joans Gas 25.1 Gas
Cash 60 Trip to Boston
Cash 34 Trip to Dayton
Joans Gas 15.75 Gas
警告STARTIN WITH 为许多SQL 解释器所支持在你喜欢上它之前请先检查你
的SQL 解释器是否支持它
gototop
 

ORDER BY 子句
在有些时候你可能会希望查询输出的结果按一定的排序规则来显示可是正如你所
知道的SELECT FROM 语句只会给你一个列表除非你已经定义了关键字见第10 天
创建视图和索引否则你查询的结果是依据它们在输入时的次序排列的请看下表
INPUT
SQL>SELECT * FROM CHECKS
OUTPUT
CHECK# PAYEE AMOUNT REMARKS
1 Ma Bell 150 Have sons next time
2 Reading R.R. 245.34 Train to Chicago
3 Ma Bell 200.32 Cellular Phone
4 Local Utilities 98 Gas
5 Joes Stale $ Dent 150 Groceries
16 Cash 25 Wild Night Out
17 Joans Gas 25.1 Gas
9 Abes Cleaners 24.35 X-Tra Starch
20 Abes Cleaners 10.5 All Dry Clean
8 Cash 60 Trip to Boston
21 Cash 34 Trip to Dayton
分析
请相信我数据输出的情况的确是按照它们被输入的先后次序排序的在第8 天的数
据操作中我们将知道如何使用INSERT 来创建一个新的表那时你就可以试一下看看
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 102
数据是不是如你所猜想的那样来排序
ORDER BY 子句为你提供了对输出的结果进行排序的方法例如将记录按CHECKS
号进行排序语句如下
INPUT
SQL>SELECT * FROM CHECKS ORDER BY CHECK#
OUTPUT
CHECK# PAYEE AMOUNT REMARKS
1 MaBell 150 Have next sonstime
2 Reading R.R. 245.34 Train to Chicago
3 Ma Bell 200.32 Cellular Phone
4 Local Utilities 98 Gas
5 Joes Stale $ Dent 150 Groceries
8 Cash 60 Trip to Boston
9 Abes Claeners 24.35 X-Tra Starch
16 Cash 25 Wild Night Out
17 Joans Gas 25.1 Gas
20 Abes Cleaners 10.5 All Dry Clean
21 Cash 34 Trip to Dayton
现在数据已经按照你的要求进行排序而不是按照它们被输入的次序进行排序了下边
的例子则表明BY 是ORDER 不可缺少的组成部分
INPUT/OUTPUT
SQL> SELECT * FROM CHECKS ORDER CHECK#
ERROR at line 1:
ORA-00924: missing BY keyword
如果你想让数据按降序排列也就是说数值最大的排在最前边那么非常幸运下例
中PAYEEs 表中的PAYEEs 列就是按降序排列的
INPUT/OUTPUT
SQL>SELECT * FROM CHECKS ORDER BY PAYEE DESC
CHECK# PAYEE AMOUNT REMARKS
2 Reading R.R. 245.34 Train to Chicago
1 Ma Bell 150 Have sons next time
3 Ma Bell 200.32 Cellular Phone
4 Local Utilities 98 Gas
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 103
5 Joes Stale $ Dent 150 Groceries
17 Joans Gas 25.1 Gas
16 Cash 25 Wild Night Out
8 Cash 60 Trip to Boston
21 Cash 34 Trip to Dayton
9 Abes Cleaners 24.35 X-Tra Starch
20 Abes Cleaners 10.5 All Dry Clean
在ORDER BY 后边的DESC 表示用降序排列来代替默认的升序排列下例则出现了
很少使用的关键字ASC 表示要按升序进行排列
INPUT
SQL>SELECT PAYEE AMOUNT FROM CHECKS ORDER BY CHECK# ASC
OUTPUT
PAYEE AMOUNT
Ma Bell 150
Reading R.R 245.34
Ma Bell 200.32
Local Utilities 98
Joes Stale $ Dent 150
Cash 60
Abes Cleaners 24.35
Cash 25
Joans Gas 25.1
Abes Cleaners 10.5
Cash 34
输出的结果与最初没有使用ASC 时是一样的这是因为ASC 是默认的选项本例也
表明了用于排序的字段并不一定要出现在SELECT 子句中尽管我们选择的是PAYEE 和
AMOUNT 但是排序却是按CHECKS 进行的
ORDER BY 可以使用多个字段下例是按PAYEE 和REMARKS 进行排序的
INPUT
SQL>SELECT * FROM CHECKS ORDER BY PAYEE REMARKS
OUTPUT
CHECK# PAYEE AMOUN
T
REMARKS
20 Abes Cleaners 10.5 All Dry Clean
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 104
9 Abes Cleaners 24.35 X-Tra Starch
8 Cash 60 Tripto Boston
21 Cash 34 Tripto Dayton
16 Cash 25 Wild Night Out
17 Joans Gas 25.1 Gas
5 Joes Stale$ Dent 150 Groceries
4 Local Utilities 98 Gas
3 Ma Bell 200.32 Cellular Phone
1 Ma Bell 150 Havesonsnexttime
2 Reading R.R. 245.34 Trainto Chicago
分析
注意在排序之前CASH在表中的输入次序CHECK号依次为16 8 21 在ORDER BY
子句中加入了REMARK字段后结果是按照REMARK的字母顺序进行排序在ORDER BY
中的列的次序对排序的结果会有影响吗试着掉换一下PAYEE 和REMARKS 的次序
INPUT
SQL>SELECT * FROM CHECKS ORDER BY REMARKS PAYEE
OUTPUT
CHECK# PAYEE AMOUNT REMARKS
20 Abes Cleaners 10.5 All Dry Clean
3 Ma Bell 200.32 Cellular Phone
17 Joans Gas 25.1 Gas
4 Local Utilities 98 Gas
5 Joes Stale $ Dent 150 Groceries
1 Ma Bell 150 Have sons next time
2 Reading R.R .245.34 Train to Chicago
8 Cash 60 Trip to Boston
21 Cash 34 Trip to Dayton
16 Cash 25 Wild Night Out
9 Abes Cleaners 24.35 X-Tra Starch
分析
你大概已经猜出来了结果是完全不相同的下例显示了如何将一列按字母的正顺排
列而把第二列按字母的逆序进行排列
INPUT/OUTPUT
SQL> SELECT * FROM CHECKS ORDER BY PAYEE ASC REMARKS DESC
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 105
CHECK# PAYEE AMOUNT REMARKS
9 Abes Cleaners 24.35 X-Tra Starch
20 Abes Cleaners 10.5 All Dry Clean
16 Cash 25 Wild Night Out
21 Cash 34 Trip to Dayton
8 Cash 60 Trip to Boston
17 Joans Gas 25.1 Gas
5 Joes Stale $ Dent 150 Groceries
4 Local Utilities 98 Gas
1 Ma Bell 150 Have sons next time
3 Ma Bell 200.32 Cellular Phone
2 Reading R.R .245.34 Train to Chicago
分析
在这个例子中PAYEE 按正序排列而REMARK 按逆序排列请注意CASH 中的
REMARK是怎样相对于PAYEE 排序的
技巧假如你已经知道了你想要进行排序的列是表中的第一列的话那么你可以用ORDER
BY 1 来代替输入列的名字见下例
INPUT/OUTPUT
SQL> SELECT * FROM CHECKS ORDER BY 1
CHECK# PAYEE AMOUNT REMARKS
1 Ma Bell 150 Have sons next time
2 Reading R.R .245.34 Train to Chicago
3 Ma Bell 200.32 Cellular Phone
4 Local Utilities 98 Gas
5 Joes Stale $ Dent 150 Groceries
8 Cash 60 Trip to Boston
9 Abes Cleaners 24.35 X-Tra Starch
16 Cash 25 Wild Night Out
17 Joans Gas 25.1 Gas
20 Abes Cleaners 10.5 All Dry Clean
21 Cash 34 Trip to Dayton
分析
它的结果与你在今天的早些时候写下的这个语句的结果是一样的
SELECT * FROM CHECKS ORDER BY CHECK#
gototop
 

GROUP BY 子句
在第三天时我们学习了汇总类函数COUNT SUM AVG MIN MAX 如果你想
看一下支出的总费用你可以用如下语句
INPUT
SELECT * FROM CHECKS
下表是修改以后的CHECKS 表
CHECKNUM PAYEE AMOUNT REMARKS
1 Ma Bell 150 Have sons next time
2 Reading R.R .245.34 Train to Chicago
3 Ma Bell 200.33 Cellular Phone
4 Local Utilities 98 Gas
5 Joes Stale $ Dent 150 Groceries
16 Cash 25 Wild Night Out
17 Joans Gas 25.1 Gas
9 Abes Cleaners 24.35 X-Tra Starch
20 Abes Cleaners 10.5 All Dry Clean
8 Cash 60 Trip to Boston
21 Cash 34 Trip to Dayton
30 Local Utilities 87.5 Water
31 Local Utilities 34 Sewer
25 Joans Gas 15.75 Gas
你会输入如下语句
INPUT/OUTPUT
SELECT SUM AMOUNT FROM CHECKS
SUM
1159.87
分析
这条语句返回了对AMOUNT 列的合计结果可是如果你想知道的是对每一个PAYEE
花了多少钱时又该怎么办呢使用GROUP BY 语句可以帮助你对本例它的使用方法如

INPUT/OUTPUT
SELECT PAYEE SUM AMOUNT FROM CHECKS GROUP BY PAYEE
PAYEE SUM
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 107
Abes Cleaners 34.849998
Cash 119
Joans Gas 40.849998
Joes Stale $ Dent 150
Local Utilities 219.5
Ma Bell 350.33002
Reading R.R .245.34
SELECT 子句有一个正常的列之后是一个汇总函数如果它的后边只有FROM
CHECKS 子句的话那么你将会看到
INPUT/OUTPUT
SELECT PAYEE SUM AMOUNT FROM CHECKS
Dynamic SQL Error
-SQL error code = -104
-invalid column reference
分析
该信息表明SQL 无法把正常的列和汇总函数结合在一起这时就需要GROUP BY 子
句它可以对SELECT 的结果进行分组后在应用汇总函数查询SELECT * FROM CHECKS
返回了14 行而SELECT PAYEE SUM AMOUNT FROM CHECKS GROUP BY
PAYEE 则把返回的14 行分成了7 组然后对每组应用了汇总函数
INPUT/OUTPUT
SELECT PAYEE SUM AMOUNT COUNT PAYEE FROM CHECKS
GROUP BY PAYEE
PAYEE SUM COUNT
Abes Cleaners 34.849998 2
Cash 119 3
Joans Gas 40.849998 2
Joes Stale $ Dent 150 1
Local Utilities 219.5 3
Ma Bell 350.33002 2
Reading R.R .245.34 1
分析
SQL 现在越来越变得有用了在上一个例子中我们只是应用GROUP BY 对数据结
果进行了唯一的分组注意结果是按PAYEE 排序的GROUP BY 也可以像ORDER BY 那
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 108
样工作如果我们对多个列进行分组会有什么结果呢请看
INPUT/OUTPUT
SELECT PAYEE SUM AMOUNT COUNT PAYEE FROM CHECKS
GROUP BY PAYEE REMARKS
PAYEE SUM COUNT
Abes Cleaners 10.5 1
Abes Cleaners 24.35 1
Cash 60 1
Cash 34 1
Cash 25 1
Joans Gas 40.849998 2
Joes Stale $ Dent 150 1
Local Utilities 98 1
Local Utilities 34 1
Local Utilities 87.5 1
Ma Bell 200.33 1
Ma Bell 150 1
Reading R.R .245.34 1
分析
输出结果由原来的将14 行分成7 组变成了13 组为什么它会多出了这么多组呢我
们来看一下
INPUT/OUTPUT
SELECT PAYEE REMARKS FROM CHECKS WHERE PAYEE Joans Gas
PAYEE REMARKS
Joans Gas Gas
Joans Gas Gas
分析
你可以看到这两个记录的内容是完全一样的所以在运行GROUP BY 以后把它们合并
成了一个记录而其它行则是唯一的所以合并以后仍然是唯一的
下例是对REMARKS 进行分组并找出组中的最大值和最小值
INPUT/OUTPUT
SELECT MIN AMOUNT MAX AMOUNT FROM CHECKS GROUP BY
REMARKS
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 109
MIN MAX
245.34 245.34
10.5 10.5
200.33 200.33
15.75 98
150 150
150 150
34 34
60 60
34 34
87.5 87.5
25 25
24.35 24.35
如果我们在分组时指定的列名与SELECT 中所指定的列名不相同时会有什么情况发生

INPUT/OUTPUT
SELECT PAYEE MAX AMOUNT MIN AMOUNT FROM CHECKS
GROUP BY REMARKS
Dynamic SQL Error
-SQL error code = -104
-invalid column reference
分析
查询无法对REMARK 进行分组当查询在REMARK 字段中找到了两个重复的数值
但它们的PAYEE 不同这表明GAS 有两个PAYEE 这将会导致错误的产生
规则是当要求分组结果返回多个数值时不能在在SELECT 子句中使用除分组列以外
的列这将会导致错误的返回值你可以使用在SELECT 中未列出的列进行分组例如
INPUT/OUTPUT
SELECT PAYEE COUNT AMOUNT FROM CHECKS
GROUP BY PAYEE, AMOUNT;
PAYEE COUNT
Abes Cleaners 1
Abes Cleaners 1
Cash 1
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 110
Cash 1
Cash 1
Joans Gas 1
Joans Gas 1
Joes Stale $ Dent 1
Local Utilities 1
Local Utilities 1
Local Utilities 1
Ma Bell 1
Ma Bell 1
Reading R.R 1
分析
这个愚蠢的查询显示的记录与你在表中输入的记录数一样多这表明你可以在GROUP
BY 中使用AMOUNT 尽管在SELECT 中没有提到过该字段现在试着将AMOUNT 字段
从GROUN 部分移动到SELECT 部分如下例
SELECT PAYEE AMOUNT COUNT AMOUNT FROM CHECKS GROUP BY
PAYEE
Dynamic SQL Error
-SQL error code = -104
-invalid column reference
SQL 不能运行查询因为在SELECT 中出现的字段没有在GROUP BY 中指出所以我们
不得不采用下边的方法进行分组
INPUT/OUTPUT
SELECT PAYEE AMOUNT REMARKS FROM CHECKS WHERE PAYEE
Cash
PAYEE AMOUNT REMARKS
Cash 25 Wild Night Out
Cash 60 Trip to Boston
Cash 34 Trip to Dayton
如果你的用户要求你将这三行数据输出并按PAYEE 进行分组的话那么请问数据并不
重复的REMARKS 字段的内容应该放在哪里切记当进行分组以后由于这三行数据是同
一组所以结果只有一行SQL 无法在同时为你做两种工作所以它会说Error #31 Can't
do two things at once.
gototop
 

HAVING 子句
如何对你需要进行分组的数据进行限制呢这里我们使用ORGCHART 表内容如下
INPUT
SELECT * FROM ORGCHART
OUTPUT
NAME TEAM SALARY SICKLEAVE ANNUALLEAVE
ADAMS RESEARCH 34000.00 34 12
WILKES MARKETING 31000.00 40 9
STOKES MARKETING 36000.00 20 19
MEZA COLLECTIONS 40000.00 30 27
MERRICK RESEARCH 45000.00 20 17
RICHARDSON MARKETING 42000.00 25 18
FURY COLLECTIONS 35000.00 22 14
PRECOURT PR 37500.00 24 24
如果你想对输出的结果进行分组并显示每一组的平均工资你可以输入如下语句
INPUT/OUTPUT
SELECT TEAM AVG SALARY FROM ORGCHART GROUP BY TEAM
TEAM AVG
COLLECTIONS 37500.00
MARKETING 36333.33
PR 37500.00
RESEARCH 39500.00
下边的这条语句的目的是返回分组后平均工资低于38000 的组
INPUT/OUTPUT
SELECT TEAM AVG SALARY FROM ORGCHART
WHERE AVG SALARY 38000 GROUP BY TEAM
Dynamic SQL Error
-SQL error code = -104
-Invalid aggregate reference
分析
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 112
错误产生的原因是由于汇总函数不能工作在WHERE 子句中如果想要让这个查询工
作的话我们需要一些新东西――HAVING 子句输入下边的查询就会得到你想要的结果

INPUT/OUTPUT
SELECT TEAM AVG SALARY FROM ORGCHART GROUP BY TEAM
HAVING AVG SALARY 38000
TEAM AVG
COLLECTIONS 37500.00
MARKETING 36333.33
PR 37500.00
分析
HAVING 子句允许你将汇总函数作为条件但是如果HAVING 后边没有汇总函数时会
有什么结果呢看下例
INPUT/OUTPUT
SELECT TEAM AVG SALARY FROM ORGCHART GROUP BY TEAM
HAVING SALARY 38000
TEAM AVG
PR 37500.00
分析
为什么这一次的结果与上一次的不同子句HAVING AVG(SALARY) < 38000 是对每一
组的SALARY 求平均数并将数值大于38000 的组返回正像你所想到的那样HAVING
SALARY < 38000 则是用另外一种处理方式所以就会有不同的结果根据SQL 的解释规
则如果用户要求对分组数据执行HAVING SALARY < 38000 它么它会对数据库中的每
个记录均进行检查并且剔除SALARY 大于38000 的这样的话就只有PR 符合条件了
在其它组中都至少有一条SALARY 大于38000 的记录并不是所有的解释器都执行这条
语句ACCESS 就不能— — 译者
INPUT/OUTPUT
SELECT NAME TEAM SALARY FROM ORGCHART ORDER BY TEAM
NAME TEAM SALARY
FURY COLLECTIONS 35000.00
MEZA COLLECTIONS 40000.00
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 113
WILKES MARKETING 31000.00
STOKES MARKETING 36000.00
RICHARDSON MARKETING 42000.00
PRECOURT PR 37500.00
ADAMS RESEARCH 34000.00
MERRICK RESEARCH 45000.00
分析
结果就是除了PR 外所有的组都被剔除了事实上你的要求是返回组中内容
SALARY<38000 的组SQL 完全按照你的要求去做了所以你不必生气
警告在一些解释器中如果在HAVING 子句中使用了非汇总函数将会导致错误ACCESS
就是这样— — 译者在没有对你所使用的解释器做认真的检查之前不要认为这样做一定会
得到结果
HAVING 子句允许使用多个条件吗试一下
INPUT
SELECT TEAM AVG SICKLEAVE AVG ANNUALLEAVE FROM ORGCHART
GROUP BY TEAM
HAVING AVG SICKLEAVE 25 AND AVG ANNUALLEAVE 20
分析
下表显示的是按TEAM 进行分组并符合平均病假大于25 天和平均年休假少于20 天的

OUTPUT
TEAM AVG AVG
MARKETING 28 15
RESEARCH 27 15
你也可以在HAVING 中使用在SELECT 中没有指出的字段进行汇总如
INPUT/OUTPUT
SELECT TEAM AVG SICKLEAVE AVG ANNUALLEAVE FROM ORGCHART
GROUP BY TEAM HAVING COUNT TEAM 1
TEAM AVG AVG
COLLECTIONS 26 21
MARKETING 28 15
RESEARCH 27 15
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 114
该查询返回了组中成员大于1 的组虽然的SELECT 子句中没有出现COUNT TEAM
语句但是它还是在HAVING 子句中起到了它应有的作用
在HAVING 子句中也可以使用其它的逻辑操作符例如
INPUT/OUTPUT
SELECT TEAM MIN SALARY MAX SALARY FROM ORGCHART
GROUP BY TEAM HAVING AVG SALARY 37000
OR
MIN SALARY 32000
TEAM MIN MAX
COLLECTIONS 35000.00 40000.00
PR 37500.00 37500.00
RESEARCH 34000.00 45000.00
操作符IN 也可以在HAVING 子句中使用如下例
INPUT/OUTPUT
SELECT TEAM AVG SALARY FROM ORGCHART GROUP BY TEAM
HAVING TEAM IN 'PR','RESEARCH'
TEAM AVG
PR 37500.00
RESEARCH 39500.00
子句的综合应用
这一部分没有什么新的东西只是通过一些例子来向你演示如何将这些子句进行综合
的应用

找出所有CHECKS 表中对CASH和对GAS 支付的记录并按REMARKS 进行排序
INPUT
SELECT PAYEE REMARKS FROM CHECKS WHERE PAYEE='Cash'
OR REMARKS LIKE'Ga%' ORDER BY REMARKS
OUTPUT
PAYEE REMARKS
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 115
Joans Gas Gas
Joans Gas Gas
Local Utilities Gas
Cash Trip to Boston
Cash Trip to Dayton
Cash Wild Night Out
分析
这里使用了LIKE 来查找在REMARKS 中以GA 开头的内容通过使用OR 来控制WHERE
返回满足两个条件之一的内容
如果您有相同的要求并要求按PAYEE 进行分组看下例
INPUT
SELECT PAYEE REMARKS FROM CHECKS WHERE PAYEE 'Cash'
OR REMARKS LIKE'Ga%' GROUP BY PAYEE ORDER BY REMARKS
分析
这个查询将会由于无法对REMARKS 进行分组而无法工作切记无论在什么情况下
进行分组SELECT 语句中出现的字段只能是在GROUP BY 中出现过的才可以— — 除非你
在SELECT 子句中不指定任何字段
例2
使用ORGCHART 表找出病候天数少于25 天的人的工资并按名字进行排序
INPUT
SELECT NAME SALARY FROM ORGCHART WHERE SICKLEAVE<25 ORDER BY
NAME
OUTPUT
NAME SALARY
FURY 35000.00
MERRICK 45000.00
PRECOURT 37500.00
STOKES 36000.00
这是个非常简单的查询并且使你对WHERE 和ORDER BY 子句有了更新的体会
例3
仍使用ORGCHART 表对每一个TEAM 显示TEAM AVG SALARY AVG
SICKLEAVE 和AVG ANNUALLEAVE
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 116
INPUT
SELECT TEAM AVG SALARY AVG SICKLEAVE AVG ANNUALLEAVE FROM
CHECKS
GROUP BY TEAM
OUTPUT
TEAM AVG AVG AVG
COLLECTIONS 37500.00 26 21
MARKETING 36333.33 28 15
PR 37500.00 24 24
RESEARCH 39500.00 26 15
下边的查询有一些有趣的变化你想一下它会有什么结果
INPUT/OUTPUT
SELECT TEAM AVG(SALARY) AVG(SICKLEAVE) AVG(ANNUALLEAVE)
FROM ORGCHART GROUP BY TEAM ORDER BY NAME
TEAM AVG AVG AVG
RESEARCH 39500.00 27 15
COLLECTIONS 37500.00 26 21
PR 37500.00 24 24
MARKETING 36333.33 28 15
只使用ORDER BY 语句可能会为你提供一些线索
INPUT/OUTPUT
SELECT NAME TEAM FROM ORGCHART ORDER BY NAME TEAM
NAME TEAM
ADAMS RESEARCH
FURY COLLECTIONS
MERRICK RESEARCH
MEZA COLLECTIONS
PRECOURT PR
RICHARDSON MARKETING
STOKES MARKETING
WILKES MARKETING
分析
当SQL 引擎对结果进行排序时它使用的是NAME 列注意排序使用SELECT 未
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 117
指的列是完全合法的并且忽略了重复的TEAM 值这样就只得到了四个值在ORDER BY
中包括TEAM 字段是没有必要的因为NAME 本身没有任何重复的记录用下列语句也
可以得到相同的结果
INPUT/OUTPUT
SELECT NAME TEAM FROM ORGCHART ORDER BY NAME
NAME TEAM
ADAMS RESEARCH
FURY COLLECTIONS
MERRICK RESEARCH
MEZA COLLECTIONS
PRECOURT PR
RICHARDSON MARKETING
STOKES MARKETING
WILKES MARKETING
现在你可以对它做一些变化例如我们可以让其逆序排列
INPUT/OUTPUT
SELECT NAME TEAM FROM ORGCHART ORDER BY NAME DESC
NAME TEAM
WILKES MARKETING
STOKES MARKETING
RICHARDSON MARKETING
PRECOURT PR
MEZA COLLECTIONS
MERRICK RESEARCH
FURY COLLECTIONS
ADAMS RESEARCH
例4 大结局
可能用一个查询来完成每一件工作吗是的但是在许多时候它们的结果却是令人费解的
WHERE 子句与ORDER BY 子句常在对单行进行处理时看到如
INPUT/OUTPUT
SELECT * FROM ORGCHART ORDER BY NAME DESC
NAME TEAM SALARY SICKLEAVE ANNUALLEAVE
WILKES MARKETING 31000.00 40 9
STOKES MARKETING 36000.00 20 19
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 118
RICHARDSON MARKETING 42000.00 25 18
PRECOURT PR 37500.00 24 24
MEZA COLLECTIONS 40000.00 30 27
MERRICK RESEARCH 45000.00 20 17
FURY COLLECTIONS 35000.00 22 14
ADAMS RESEARCH 34000.00 34 12
GROUP BY 和HAVING 子句常用在对数据进行汇总操作上
INPUT/OUTPUT
SELECT PAYEE SUM AMOUNT TOTAL COUNT PAYEE NUMBER_WRITTEN
FROM CHECKS GROUP BY PAYEE HAVING SUM AMOUNT >50
PAYEE TOTAL NUMBER_WRITTEN
Cash 119 3
Joes Stale $ Dent 150 1
Local Utilities 219.5 3
Ma Bell 350.33002 2
Reading R.R .245.34 1
如果把它们结合起来使用会有出人意料的结果例如
INPUT
SELECT PAYEE SUM AMOUNT TOTAL COUNT PAYEE NUMBER_WRITTEN
FROM CHECKS WHERE AMOUNT>=100 GROUP BY PAYEE
HAVING SUM AMOUNT >50
OUTPUT
PAYEE TOTAL NUMBER_WRITTEN
Joes Stale $ Dent 150 1
Ma Bell 350.33002 2
Reading R.R .245.34 1
将其与下边的结果进行对比
INPUT/OUTPUT
SELECT PAYEE AMOUNT FROM CHECKS ORDER BY PAYEE
PAYEE AMOUNT
Abes Cleaners 10.5
Abes Cleaners 24.35
Cash 25
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 119
Cash 34
Cash 60
Joans Gas 15.75
Joans Gas 25.1
Joes Stale $ Dent 150
Local Utilities 34
Local Utilities 87.5
Local Utilities 98
Ma Bell 150
Ma Bell 200.33
Reading R.R .245.34
分析
你使用了WHERE 子句在分组前将AMOUNT 小于50 的记录过滤掉了我们并不试图告诉
你不要结合使用这两种分组你在以后可能会有这方面的需要但是请不要随便地结合使
用这两种子句在上例的表中只有为数不多的几行否则这本书的内容需要用车来拉了
而在你的实际工作中数据库可能有成千上万行结合使用后造成的变化就不会像现在这样
明显了
gototop
 

总结
在今天我们学习了与扩展SELECT 语句功能相关的所有子句切记要认真仔细地去对
计算机描述你的需求我们的基本SQL 教育到这里就结束了你已经有足够的能力对单个
表进行操作了明天第6 天归并表格我们将有机会在多个表中工作
问与答
问像这些功能在这一周的早些时候我们已经学习过了为什么今天还要再学习一次
答我们的确在第3 天就曾经提到过WHERE 子句我们在那时使用WHERE 是为了
更加可靠地进行操作WHERE 在今天出现是因为它是一个子句而我们在今天
讨论的主题是子句
校练场
1 哪种子句的作用与LIKE <exp>% 相似
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 120
2 GROUP BY 子句的功能是什么哪种子句的功能与它类似
3 下面的查询会工作吗
INPUT
SQL>SELECT NAME AVG SALARY DEPARTMENT FROM PAY_TBL
WHERE DEPARTMENT='ACCOUNTING' ORDER BY NAME
GROUP BY DEPARTMENT SALARY
4 为什么在使用HAVING 子句时我们总是同时使用GROUP BY 子句
5 你可以使用在SELECT 语句中没有出现的列进行排序吗
练习
1 使用上例中的ORGCHART 表找一下每一个TEAM 中SICKLEAVE 天数超过30 天
的人数
2 使用CHECKS 表返回如下结果
OUTPUT
CHECK# PAYEE AMOUNT
1 MA BELL 150
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 121
第六天表的联合
今天我们将学习联合操作这种操作可以让你从多个表中选择数据并对它们进行维护
在今天结束以后我们将会具有以下能力
l 执行外部联合
l 执行内部联合
l 执行左联合
l 执行右联合
l 进行等值联合
l 进行不等值联合
介绍
能够从多个表中选择和操作数据是SQL 的特色之一如果没有这个功能的话你将不得
不将一个应用程序所需的所有数据放在一个表中如果表不能共享那么你将不得不在多个
表中保存相同的数据而且每当用户需要查询一个新的内容时你就不得不重新设计和编
译你的数据库系统SQL 中的JION 语句可以让你的设计出比那种庞大的表格更小和更为
专业以及更容易使用的表格
在一个SELECT 语句中使用多个表
就像多萝茜在绿野仙踪中所做的一样你其实在第二天查询— — SELECT 语
句的使用中学习过SELECT 和FROM 以后就已经具备了联合多个表格的能力了但是与
多萝茜不同你执行联合操作并不需要将脚后跟磕三下使用下边的两个表简单点不
妨就叫TABEL1 和TABLE2
注在今天的查询使用的是BORLAND 的ISQL 产生的结果你会发现它与我们在本书的
早些时候所使用的查询有一些不同之处例如它没有SQL 提示符而且在语句的末尾也
没有分号在ISQL 中分号是可选项但是查询的基本结构是相同的
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 122
INPUT
SELECT * FROM TABLE1
OUTPUT
ROW REMARKS
row1 Table
Row2 Table
Row3 Table
Row4 Table
Row5 Table
Row6 Table
INPUT
SELECT * FROM TABLE2
OUTPUT
ROW REMARKS
Row1 Table2
Row2 Table2
Row3 Table2
Row4 Table2
Row5 Table2
Row6 Table2
要联合两个表格可以像下边这样操作
INPUT:
SELECT *
FROM TABLE1,TABLE2
OUTPUT
ROW REMARKS ROW REMARKS
row 1 Table 1 row 1 table 2
row 1 Table 1 row 2 table 2
row 1 Table 1 row 3 table 2
row 1 Table 1 row 4 table 2
row 1 Table 1 row 5 table 2
row 1 Table 1 row 6 table 2
row 2 Table 1 row 1 table 2
总计有36 行它们都是从哪来的呢这又属于哪一种联合类型呢
分析
认真看一下你会发现联合的结果其实就是将TABEL1 中的每一行与TABEL2 中的每
一行都接合了起来其中的一个片断如下
ROW REMARKS ROW REMARKS
row 1 Table 1 row 1 table 2
row 1 Table 1 row 2 table 2
row 1 Table 1 row 3 table 2
row 1 Table 1 row 4 table 2
SQL 21 日自学通(V1.0) 翻译人笨猪
123
ROW REMARKS ROW REMARKS
row 1 Table 1 row 5 table 2
row 1 Table 1 row 6 table 2
看TABEL2 中的每一行均与TABEL1 中的第一行联合了起来祝贺你你已经完成
了你的第一个联合可是它是哪一种联合呢内部联合外部联合还是别的嗯其
实这种联合应该称为交叉联合其实就是笛卡尔叉积— — 译者交叉联合在今天不像其它
联合那样有用但是这种联合表明了联合的最基本属性联合源自表格
假设你为生计所迫到一家自行车行中卖零件在你设计一个数据库时你建立了一个
大表其中囊括了所有的相关列每当你有一个新的需要时你就向其中加入了一个新列
或者是重新建立一张表向其中加入所有以前的数据后再建立一个特定的查询最后你
的数据库将会由于它自身的重量而崩溃— — 你不想看到这种情况所以此外的选择就是使
用关系模型你只需所相关的数据放入同一张表中下边显示的是你的客户表
INPUT
SELECT * FROM CUSTOMER
OUTPUT
NAME ADDRESS STATE ZIP PHONE REMARKS
TRUE WHEEL 55O HUSKER NE 58702 555-4545 NONE
BIKE SPEC CPT SHRIVE LA 45678 555-1234 NONE
LE SHOPPE HOMETOWN KS 54678 555-1278 NONE
AAA BIKE 10 OLDTOWN NE 56784 555-3421 JOHN-MGR
JACKS BIKE 24 EGLIN FL 34567 555-2314 NONE
分析
这张表中包括了所有的你需要对顾客进行的描述而关于你所卖的产品则在另外一张
表上
INPUT
SELECT * FROM PART
OUTPUT
PARTNUM DESCRIPTION PRICE
54 PEDALS 54.25
42 SEATS 24.50
46 TIRES 15.25
23 MOUNTAIN BIKE 350.45
SQL 21 日自学通(V1.0) 翻译人笨猪
124
76 ROAD BIKE 530.00
10 TANDEM 1200.00
而你的定单则有着它们自己的表
INPUT
SELECT * FROM ORDERS
OUTPUT
ORDEREDON NAME PARTNUM QUANTITY REMARKS
15-MAY-1996 TRUE WHEEL 23 6 PAID
19-MAY-1996 TRUE WHEEL 76 3 PAID
2-SEP-1996 TRUE WHEEL 10 1 PAID
30-JUN-1996 TRUE WHEEL 42 8 PAID
30-JUN-1996 BIKE SPEC 54 10 PAID
30-MAY-1996 BIKE SPEC 10 2 PAID
30-MAY-1996 BIKE SPEC 23 8 PAID
17-JAN-1996 BIKE SPEC 76 11 PAID
17-JAN-1996 LE SHOPPE 76 5 PAID
1-JUN-1996 LE SHOPPE 10 3 PAID
1-JUN-1996 AAA BIKE 10 1 PAID
1-JUL-1996 AAA BIKE 76 4 PAID
1-JUL-1996 AAA BIKE 46 14 PAID
11-JUL-1996 JACKS BIKE 76 14 PAID
这样做的好处是你可以用三个专职人员或部门来维护属于他们自己的数据你也无需
与数据库管理员来套交情好让他看管你那庞大的多部门的数据库另外的优点就是由于
网路的发展每个表都可以放在不同的机器上所有它可以在适当的地点由对它的内部数
据熟悉的人来进行维护而不是像大型机那样需要一队的专家来进行维护
现在将PARTS 表与ORDERS 表进行联合
INPUT/OUTPUT
SELECT O.ORDEREDON O.NAME O.PARTNUM P.PARTNUM P.DESCRIPTION
FROM ORDERS O PART P
ORDEREDON NAME PARTNUM PARTNUM DESCRIPTION
15-MAY-1996 TRUE WHEEL 23 54 PEDALS
19-MAY-1996 TRUE WHEEL 76 54 PEDALS
2-SEP-1996 TRUE WHEEL 10 54 PEDALS
30-JUN-1996 TRUE WHEEL 42 54 PEDALS
SQL 21 日自学通(V1.0) 翻译人笨猪
125
ORDEREDON NAME PARTNUM PARTNUM DESCRIPTION
30-JUN-1996 BIKE SPEC 54 54 PEDALS
30-MAY-1996 BIKE SPEC 10 54 PEDALS
30-MAY-1996 BIKE SPEC 23 54 PEDALS
17-JAN-1996 BIKE SPEC 76 54 PEDALS
17-JAN-1996 LE SHOPPE 76 54 PEDALS
1-JUN-1996 LE SHOPPE 10 54 PEDALS
1-JUN-1996 AAA BIKE 10 54 PEDALS
1-JUL-1996 AAA BIKE 76 54 PEDALS
1-JUL-1996 AAA BIKE 46 54 PEDALS
11-JUL-1996 JACKS BIKE 76 54 PEDALS
分析
上表只是结果集的一部分实际上记录数应该有14 定单行数6 零件行数=84
行它与今天的早些时候TABEL1 与TABEL2 的联合类似这条语句的结果仍然没有太大
的用处在我们对这种语句深入之前我们先回想并讨论一下别名的问题
gototop
 

正确地找到列
当你将TABLE1 与TABLE2 联合以后你使用SELECT * 来选择了表中的所有列在
联合表ORDER 和PART 时SELECT 看起来不太好懂
SELECT O.ORDEREDON O.NAME O.PARTNUM P.PARTNUM P.DESCRIPTION
SQL 可以知道ORDEREDON 和NAME 是在ORDER 表中而DESCRIPTION 则存在于
PART 表中但是PARTNUM 呢它在两个表中都有啊如果你想使用在两个表中都存在
的列你必须使用别名来说明你想要的是哪一列常用的办法为每一个表分配一个简单的
字符就像你在FROM 子句中所做的那样
FROM ORDERS O PART P
你可以在每一列中都使用这个字符就像你刚才在SELECT 中所做的那样SELECT
子句也可以写成下边的形式
SELECT ORDEREDON NAME O.PARTNUM P.PARTNUM DESCRIPTION
可是不要忘记有时你会不得不回过头来对查询进行维护所以让它更具有可读性并
没有什么害处还是不要使用这种省略的形式吧
SQL 21 日自学通(V1.0) 翻译人笨猪
126
等值联合
下边的表是ORDERS 与PARTS 表的联合结果的片断作为缺货的情况
30-JUN-1996 TRUEWHEEL 42 54 PEDALS
30-JUN-1996 BIKESPEC 54 54 PEDALS
30-MAY-1996 BIKESPEC 10 54 PEDALS
注意到PARTNUM 是两个表的共有字段如果输入如下的语句会有什么结果呢
INPUT
SELECT O.ORDEREDON O.NAME O.PARTNUM P.PARTNUM P.DESCRIPTION
FROM ORDERS O PART P WHERE O.PARTNUM P.PARTNUM
OUTPUT
ORDEREDON NAME PARTNUM PARTNUM DESCRIPTION
1-JUN-1996 AAA BIKE 10 10 TANDEM
30-MAY-1996 BIKE SPEC 10 10 TANDEM
2-SEP-1996 TRUE WHEEL 10 10 TANDEM
1-JUN-1996 LE SHOPPE 10 10 TANDEM
30-MAY-1996 BIKE SPEC 23 23 MOUNTAIN BIKE
15-MAY-1996 TRUE WHEEL 23 23 MOUNTAIN BIKE
30-JUN-1996 TRUE WHEEL 42 42 SEATS
1-JUL-1996 AAA BIKE 46 46 TIRES
30-JUN-1996 BIKE SPEC 54 54 PEDALS
1-JUL-1996 AAA BIKE 76 76 ROAD BIKE
17-JAN-1996 BIKE SPEC 76 76 ROAD BIKE
19-MAY-1996 TRUE WHEEL 76 76 ROAD BIKE
11-JUL-1996 JACKS BIKE 76 76 ROAD BIKE
17-JAN-1996 LE SHOPPE 76 76 ROAD BIKE
分析
利用在两个表中都存在的PARTNUM 列我们得到了存储在ORDERS 表中的的信息
以及在PARTS 中的与ORDERS 相关的信息它表明了你已经定出的零件数量这种联合
操作称为等值联合因为它只显示第一个表中的数据以及第二个表中的存在于第一个表
中的数值
你也可以使用WHERE 子句对其结果进行更大的限制例如
INPUT/OUTPUT
SELECT O.ORDEREDON O.NAME O.PARTNUM P.PARTNUM P.DESCRIPTION
SQL 21 日自学通(V1.0) 翻译人笨猪
127
FROM ORDERS O PARTP WHERE O.PARTNUM P.PARTNUM
AND O.PARTNUM=76
ORDEREDON NAME PARTNUM PARTNUMDES DESCRIPTION
1-JUL-1996 AAABIKE 76 76 ROADBIKE
17-JAN-1996 BIKESPEC 76 76 ROADBIKE
19-MAY-1996 TRUEWHEEL 76 76 ROADBIKE
11-JUL-1996 JACKSBIKE 76 76 ROADBIKE
17-JAN-1996 LESHOPPE 76 76 ROADBIKE
PARTNUM 为76 的零件描述不是非常准确你不想把它作为零件出售我们非常愦憾
在发现在许多数据库系统中需要最终用户知道一些非常晦涩的代码而该代码所代表的东
西原本就有着自己的非常清楚明白的名字请不要像他们那样做这一行代码也可以
写成如下方式
INPUT/OUTPUT
SELECT O.ORDEREDON O.NAME O.PARTNUM P.PARTNUM P.DESCRIPTION
FROM ORDERS O PART P WHERE O.PARTNUM P.PARTNUM
AND P.DESCRIPTION ROAD BIKE
ORDEREDON NAME PARTNUM PARTNUMDES DESCRIPTION
1-JUL-1996 AAABIKE 76 76 ROADBIKE
17-JAN-1996 BIKESPEC 76 76 ROADBIKE
19-MAY-1996 TRUEWHEEL 76 76 ROADBIKE
11-JUL-1996 JACKSBIKE 76 76 ROADBIKE
17-JAN-1996 LESHOPPE 76 76 ROADBIKE
顺着这个思路我们来看一下一个或多个表是如何进行联合的在下边的例子中
employee_id 显然是唯一标识列因为你可以有在同一个公司有相同薪水并且他们的名字
也相同的雇员但是他们会各自拥有他们自己的employee_id 所以如果要对这两个表进行
联合我们应该使用employee_id 列
EMPLOYEE_TABLE EMPLOYEE_PAY_TABLE
EMPLOYEE_ID EMPLOYEE_ID
LAST_NAME SALARY
FIRST _NAME DEPARTMENT
MIDDLE_NAME SUPERVISOR
MARITAL_STATUS
INPUT
SQL 21 日自学通(V1.0) 翻译人笨猪
128
SELECT E.EMPLOYEE_ID E.LAST_NAME EP.SALARY FROM EMPLOYEE_TBL E
EMPLOYEE_PAY_TBL EP WHERE E.EMPLOYEE_ID = EP.EMPLOYEE_ID
AND E.LAST_NAME = 'SMITH';
OUTPUT
E.EMPLOYEE_ID E.LAST_NAME EP.SALARY
13245 SMITH 35000.00
技巧如果你在联合表的时候没有使用WHERE 子句你执行的其实是笛卡尔联合也就
是笛卡尔叉积这种联合会对FROM 中指出的表进行完全的组合如果每个表有200
个记录的话那么所得到的结果将会有40000 行200 200 这太大了所以除非
你确实是想对表中的所有记录进行联合否则一定不要忘记使用WHERE 子句
现在回要原来的表中我们已经对联合进行了充分的准备可以用它来完成一些实际
的工作了找一下我们卖road bikes 共卖了多少钱
INPUT/OUTPUT
SELECT SUM O.QUANTITY * P.PRICE TOTAL FROM ORDERS O, PART P
WHERE O.PARTNUM = P.PARTNUM AND P.DESCRIPTION = 'ROAD BIKE'
TOTAL
19610.00
在这种设置中销售人员可以保证ORDERS 表的更新生产部门则可以保持PART 表
的更新而你则无需对数据库的底层进行重新设计
注注意在SQL 语句中表以及列的别名的使用你可能会因为别名多按了许多许多个按键
但是它可以让你的语句更具有可读性
我们可以对更多的表进行联合吗例如我们需要生成发票所要的信息可以这样写
INPUT/OUTPUT
SELECT C.NAME C.ADDRESS O.QUANTITY * P.PRICE TOTAL
FROM ORDER O PART P CUSTOMER C
WHERE O.PARTNUM = P.PARTNUM AND O.NAME = C.NAME
NAME ADDRESS TOTAL
TRUE WHEEL 55O HUSKER 1200.00
BIKE SPEC CPT SHRIVE 2400.00
SQL 21 日自学通(V1.0) 翻译人笨猪
129
LE SHOPPE HOMETOWN 3600.00
AAA BIKE 10 OLDTOWN 1200.00
TRUE WHEEL 55O HUSKER 2102.70
BIKE SPEC CPT SHRIVE 2803.60
TRUE WHEEL 55O HUSKER 196.00
AAA BIKE 10 OLDTOWN 213.50
BIKE SPEC CPT SHRIVE 542.50
TRUE WHEEL 55O HUSKER 1590.00
BIKE SPEC CPT SHRIVE 5830.00
JACKS BIKE 24 EGLIN 7420.00
LE SHOPPE HOMETOWN 2650.00
AAA BIKE 10 OLDTOWN 2120.00
把语句写成如下格式会更具有可读性
INPUT/OUTPUT
SELECT C.NAME C.ADDRESS O.QUANTITY * P.PRICE TOTAL
FROM ORDERS O PART P CUSTOMER C
WHERE O.PARTNUM = P.PARTNUM
AND O.NAME = C.NAME ORDER BY C.NAME
NAME ADDRESS TOTAL
AAA BIKE 10 OLDTOWN 213.50
AAA BIKE 10 OLDTOWN 2120.00
AAA BIKE 10 OLDTOWN 1200.00
BIKE SPEC CPT SHRIVE 542.50
BIKE SPEC CPT SHRIVE 2803.60
BIKE SPEC CPT SHRIVE 5830.00
BIKE SPEC CPT SHRIVE 2400.00
JACKS BIKE 24 EGLIN 7420.00
LE SHOPPE HOMETOWN 2650.00
LE SHOPPE HOMETOWN 3600.00
TRUE WHEEL 55O HUSKER 196.00
TRUE WHEEL 55O HUSKER 2102.70
TRUE WHEEL 55O HUSKER 1590.00
TRUE WHEEL 55O HUSKER 1200.00
注注意当将三个表进行联合的时候ORDERS PART CUSTOMER ORDERS 表
SQL 21 日自学通(V1.0) 翻译人笨猪
130
被使用了两次而其它的表只使用了一次通常根据给定的条件返回行数最少的表会作
为驱动表— — 也就是基表在查询中除基表以外的其它表通常是向基表联合以便更有效地
获得数据所以在本例中ORDERS 表是基表在大多数的数据库中只有很少的几个基表
直接或间接地与其它的所有表联合见第15 天高性能的SQL 语句流for more on
base tables.
在下边的使用中我们通过使用DESCRIPTION 列来使上述的查询更精确因而也就更
有效
INPUT/OUTPUT
SELECT C.NAME C.ADDRESS O.QUANTITY * P.PRICE TOTAL P.DESCRIPTION
FROM ORDERS O PART P CUSTOMER C
WHERE O.PARTNUM=P.PARTNUM AND O.NAME = C.NAME ORDER BY C.NAME
NAME ADDRESS TOTAL DESCRIPTION
AAA BIKE 10 OLDTOWN 213.50 TIRES
AAA BIKE 10 OLDTOWN 2120.00 ROAD BIKE
AAA BIKE 10 OLDTOWN 1200.00 TANDEM
BIKE SPEC CPT SHRIVE 542.50 PEDALS
BIKE SPEC CPT SHRIVE 2803.60 MOUNTAIN BIKE
BIKE SPEC CPT SHRIVE 5830.00 ROAD BIKE
BIKE SPEC CPT SHRIVE 2400.00 TANDEM
JACKS BIKE 24 EGLIN 7420.00 ROAD BIKE
LE SHOPPE HOMETOWN 2650.00 ROAD BIKE
LE SHOPPE HOMETOWN 3600.00 TANDEM
TRUE WHEEL 55O HUSKER 196.00 SEATS
TRUE WHEEL 55O HUSKER 2102.70 MOUNTAIN BIKE
TRUE WHEEL 55O HUSKER 1590.00 ROAD BIKE
TRUE WHEEL 55O HUSKER 1200.00 TANDEM
分析
这是三个表联合后的结果我们可以所得到的信息来开发票了
注在今天的开始SQL 曾经联合过TABEL1 和TABEL2 并生成了一个新表有X(TABLE1
的行数) Y(TABLE2 的行数)列联合并没有生成确实存在的表格但它生成了一个虚
拟的表格对两个表联合包括自我联合后会根据WHERE 所指定的条件生成一个
新的集合SELECT 语句减少了显示的列数但是WHERE 语句仍然把所有的列全返
回了在今天的例子中我们的表中只有为数不多的几列而现实生活中的数据可能会
SQL 21 日自学通(V1.0) 翻译人笨猪
131
有成千上万列如果你所使用的平台足够快那么多表联合可能对系统的性能没有影
响可是如果你工作在一个比较慢的平台上联合可能会导致死机
gototop
 

不等值联合
既然SQL 支持等值联合你也许会推想它也支持不等值联合你猜对了等值联合是
在WHERE 子句中使用等号而不等值联合则是在WHERE 子句中使用除了等号以外的其
它比较运算符现下例
INPUT
SELECT O.NAME O.PARTNUM P.PARTNUM O.QUANTITY * P.PRICE TOTAL
FROM ORDERS O PART P WHERE O.PARTNUM > P.PARTNUM
OUTPUT
NAME PARTNUM PARTNUM TOTAL
TRUE WHEEL 76 54 162.75
BIKE SPEC 76 54 596.75
LE SHOPPE 76 54 271.25
AAA BIKE 76 54 217.00
JACKS BIKE 76 54 759.50
TRUE WHEEL 76 42 73.50
BIKE SPEC 54 42 245.00
BIKE SPEC 76 42 269.50
LE SHOPPE 76 42 122.50
AAA BIKE 76 42 98.00
AAA BIKE 46 42 343.00
JACKS BIKE 76 42 343.00
TRUE WHEEL 76 46 45.75
BIKE SPEC 54 46 152.50
BIKE SPEC 76 46 167.75
LE SHOPPE 76 46 76.25
AAA BIKE 76 46 61.00
JACKS BIKE 76 46 213.50
TRUE WHEEL 76 23 1051.35
TRUE WHEEL 42 23 2803.60
分析
上边的表给出了满足条件WHERE O.PARTNUM > P.PARTNUM 的所有联合内容结合
SQL 21 日自学通(V1.0) 翻译人笨猪
132
你上边的自行车行的例子这些信息似乎没有太多的意义在现实世界中等值联合的使用
要远远多于不等值联合但是你的编程时可能会遇到使用不等值联合的情况
外部联合与内部联合
就像不等值联合与等值联合相对应一样外部联合是与内部联合相对应的内部联合
是指与个表内的行与本表内的数据相互进行联合产生的结果行数取决于参加联合的行数
也就是说内部联合的行数取决于WHERE 子句的结果外部联合则是表间的联合如上例
中的ORDERS 表与PART 表的联合内部联合的例子如下
INPUT
SELECT P.PARTNUM P.DESCRIPTION P.PRICE O.NAME O.PARTNUM
FROM PART P JOIN ORDERS O ON ORDERS.PARTNUM = 54
OUTPUT
PARTNUM DESCRIPTION PRICE NAME PARTNUM
54 PEDALS 54.25 BIKESPEC 54
42 SEATS 24.50 BIKESPEC 54
46 TIRES 15.25 BIKESPEC 54
23 MOUNTAIN BIKE 350.45 BIKESPEC 54
76 ROAD BIKE 530.00 BIKESPEC 54
10 TANDEM 1200.00 BIKESPEC 54
注在这里你使用的语法中的JOIN ON 不是ANSI 标准中所指定的而是我们所使用的解
释器的附加语法你可以用它来指明是内部联合还是外部联合大多数解释器对些都进行
了类似的扩充注意这种类型的联合没有WHERE 子句
分析
结果表明PART 表中的所有的行都与PARTNUM 为54 的行进行了组合再来看一个
外部右联合的例子
INPUT/OUTPUT
SELECT P.PARTNUM P.DESCRIPTION P.PRICE O.NAME O.PARTNUM FROM PART P
RIGHT OUTER JOIN ORDERS O ON ORDERS.PARTNUM = 54
PARTNUM DESCRIPTION PRICE NAME PARTNUM
SQL 21 日自学通(V1.0) 翻译人笨猪
133
PARTNUM DESCRIPTION PRICE NAME PARTNUM
<null> <null> <null> TRUE WHEEL 23
<null> <null> <null> TRUE WHEEL 76
<null> <null> <null> TRUE WHEEL 10
<null> <null> <null> TRUE WHEEL 42
54 PEDALS 54.25 BIKE SPEC 54
42 SEATS 24.50 BIKE SPEC 54
46 TIRES 15.25 BIKE SPEC 54
23 MOUNTAIN BIKE 350.45 BIKE SPEC 54
76 ROAD BIKE 530.00 BIKE SPEC 54
10 TANDEM 1200.00 BIKE SPEC 54
<null> <null> <null> BIKE SPEC 10
<null> <null> <null> BIKE SPEC 23
<null> <null> <null> BIKE SPEC 76
<null> <null> <null> LE SHOPPE 76
<null> <null> <null> LE SHOPPE 10
<null> <null> <null> AAA BIKE 10
<null> <null> <null> AAA BIKE 76
<null> <null> <null> AAA BIKE 46
<null> <null> <null> JACKS BIKE 76
分析
这是一种新型的查询这里我们第一次使用了RIGHT OUTER JOIN 它会令SQL 返
回右边表集内的全部记录如果当ORDERS.PARTNUM<>54 则补以空值下边是一个左
联合的例子
INPUT/OUTPUT:
SELECT P.PARTNUM P.DESCRIPTION P.PRICE O.NAME O.PARTNUM
FROM PART P LEFT OUTER JOIN ORDERS O ON ORDERS.PARTNUM = 54
PARTNUM DESCRIPTION PRICE NAME PARTNUM
54 PEDALS 54.25 BIKE SPEC 54
42 SEATS 24.50 BIKE SPEC 54
46 TIRES 15.25 BIKE SPEC 54
23 MOUNTAIN BIKE 350.45 BIKE SPEC 54
76 ROAD BIKE 530.00 BIKE SPEC 54
10 TANDEM 1200.00 BIKE SPEC 54
分析
SQL 21 日自学通(V1.0) 翻译人笨猪
134
与内部联合的结果一样都是六行因为你使用的是左联合PART 表决定返回的行
数而PART 表比ORDERS 表小所以SQL 把其余的行数都扔掉了
不要对内部联合和外部联合操太多的心大多数的SQL 产品会判断应该在你的查询中
使用哪一种联合事实上如果你在过程中使用它或在程序内使用这包括存储过程和
将在第13 天提到的高级SQL 使用你无需指明联合类型解释器会为你选择合适的
语法形式如果你指明的联合类型解释器会用你指明的类型来代替优化的类型
在一些解释器中使用+号来代替外部联合+号的意思就是— — 显示我的全部内容包括
不匹配的内容语法如下
SYNTAX
SQL> select e.name e.employee_id ep.salary ep.marital_status from e,ployee_tbl e,
employee_pay_tbl ep
where e.employee_id = ep.employee_id(+) and e.name like '%MITH'
分析
这条语句将会联合两个表标有+号的employee_id 将会全部显示包括不满足条件的
记录
表的自我联合
今天的最后一个内容是经常使用的自我联合它的语法与联合两个表的语法相似例
如表1 的自我联合可以写成如下格式
INPUT
SELECT * FROM TABLE1 TABLE1
OUTPUT
ROW REMARKS ROW REMARKS
row 1 Table 1 row 1 Table 1
row 1 Table 1 row 2 Table 1
row 1 Table 1 row 3 Table 1
row 1 Table 1 row 4 Table 1
row 1 Table 1 row 5 Table 1
row 1 Table 1 row 6 Table 1
row 2 Table 1 row 1 Table 1
row 2 Table 1 row 2 Table 1
SQL 21 日自学通(V1.0) 翻译人笨猪
135
ROW REMARKS ROW REMARKS
row 2 Table 1 row 3 Table 1
row 2 Table 1 row 4 Table 1
row 2 Table 1 row 5 Table 1
row 2 Table 1 row 6 Table 1
分析
如果把这个表的内容全部列出的话它与联合两个有6 行的表是相同的这种联合
对于检查内部数据的一致性如果你的零件生产部门的某人犯了迷糊输入了一个已经存在
的零件号时将会发生什么呢这对于每一个人来说都是一个坏消息发票会开错你的应
用程序会崩溃会耗掉你许多宝贵的时光在下表中重复的PARTNUM 会导致问题的产

INPUT/OUTPUT
SELECT * FROM PART
PARTNUM DESCRIPTION PRICE
54 PEDALS 54.25
42 SEATS 24.50
46 TIRES 15.25
23 MOUNTAIN BIKE 350.45
76 ROAD BIKE 530.00
10 TANDEM 1200.00
76 CLIPPLESS SHOE 65.00<-NOTESAME#
下边的语句会使你的公司从不利的局面中摆脱出来
INPUT/OUTPUT
SELECT F.PARTNUM F.DESCRIPTION S.PARTNUM S.DESCRIPTION
FROM PART F PART S WHERE F.PARTNUM = S.PARTNUM
AND F.DESCRIPTION <> S.DESCRIPTION
PARTNUM DESCRIPTION PARTNUM DESCRIPTION
76 ROAD BIKE 76 CLIPPLESS SHOE
76 CLIPPLESS SHOE 76 ROAD BIKE
分析
直到有人问你为什么这个表的记录会是两个之前你会是一个英雄你会记得你曾经学
习过联合是语句
WHERE F.PARTNUM = S.PARTNUM AND F.DESCRIPTION <> S.DESCRIPTION.
使你有了英雄的称号当然表中重复的记录内容将会被更正
gototop
 

总结
今天你学习了对选择的表进行了所有的可能的联合所得的结果是满足你所提出的选
择的信息的内容
没有了— — 现在你已经学习了关于SELECT 语句的几乎所有的内容剩下的一个内容
— — 子查询将会在明天提到第7 天内嵌了SELECT 语句
问与答
问既然我不可能用到这些联合那为什么还要讲它们呢
答一知半解是危险的你为无知付出的代价是昂贵的现在你已经有了足够的知
识来了解SQL 引擎在优化你的查询时所做的基本工作
问我可以对多少个表进行联合
答依解释器而定有一些解释器有25 个表的限制另外一些则没有这个限制但是
请不要忘记你联合的表越多系统的响应就会越慢为安全起见请检查你的
解释器看一看它最多允许同时使用多少个表
问为什么说将多个表联合为一个表的说法是不对的
答很简单因为并没有这种事情发生当你联合时你只是从多个表中选出了特定
的列
校练场
1 如果一个表有50000 行而另一个表有100000 行时联合的结果会有多少行
2 下边的联合属于哪一种类型的联合
SELECT E.NAME, E.EMPLOYEE_ID, EP.SALARY FROM EMPLOYEE_TBL E, EMPLOYEE_PAY_TBL EP
WHERE E.EMPLOYEE_ID = EP.EMPLOYEE_ID
3 下边的查询语句能否工作
A. SELECT NAME, EMPLOYEE_ID, SALARY FROM EMPLOYEE_TBL E, EMPLOYEE_PAY_TBL EP
WHERE EMPLOYEE_ID = EMPLOYEE_ID AND NAME LIKE '%MITH';
SQL 21 日自学通(V1.0) 翻译人笨猪
137
B. SELECT E.NAME, E.EMPLOYEE_ID, EP.SALARY FROM EMPLOYEE_TBL E, EMPLOYEE_PAY_TBL EP
WHERE NAME LIKE '%MITH';
C. SELECT E.NAME, E.EMPLOYEE_ID, EP.SALARY FROM EMPLOYEE_TBL E,EMPLOYEE_PAY_TBL EP
WHERE E.EMPLOYEE_ID = EP.EMPLOYEE_ID AND E.NAME LIKE '%MITH';
4 是否在联合语句中WHERE 子句中的第一个条件应该是联合条件
5 联合的限制为一列是否可以有更多的列
练习
1 在表的自我联合这部分最后的一个例子返回了两个结果请重写这个查询使它对
多余的记录只返回一个结果
2 重写下边的查询使它更可读和简炼
INPUT:
select orders.orderedon, orders.name, part.partnum,part.price, part.description
from orders, part
where orders.partnum = part.partnum and orders.orderedon
between '1-SEP-96' and '30-SEP-96' order by part.partnum
3 使用ORDERS 表和PART 表返回下边的结果
OUTPUT
ORDEREDON NAME PARTNUM QUANTITY
2-SEP-96 TRUE WHEEL 10 1
gototop
 

第七天子查询内嵌的SQL 子句
目标
子查询是一种把查询的结果作为参数返回给另一个查询的一种查询子查询可以让你
将多个查询绑定在一起到今天结束以后你将掌握以下内容
l 建立一个子查询
l 在你的子查询中使用EXIST ANY和ALL 关键字
l 建立和使用子查询的关联
注今天的例子是使用BORLAND 公司的ISQL 建立的我们在第六天使用的也是这种解
释器切记这种查询没有SQL>提示符以及行号
建立一个子查询
简而言之子查询可以让你把查询的结果与另一个查询绑定在一起通用的语法格式
如下
SYNTAX
SELECT * FROM TABLE1 WHERE TABLE1.SOMECOLUMN =
(SELECT SOMEOTHERCOLUMN FROM TABLE2
WHERE SOMEOTHERCOLUMN = SOMEVALUE)
注意一下第二个查询是如何嵌入到第一个查询之中的这里用ORDERS 和PART 表来
举一个实例
INPUT
SELECT * FROM PART
OUTPUT
PARTNUM DESCRIPTION PRICE
54 PEDALS 54.25
42 SEATS 24.50
46 TIRES 15.25
SQL 21 日自学通(V1.0) 翻译人笨猪
139
PARTNUM DESCRIPTION PRICE
23 MOUNTAIN BIKE 350.45
76 ROAD BIKE 530.00
10 TANDEM 1200.00
INPUT/OUTPUT
SELECT *
FROM ORDERS
ORDEREDON NAME PARTNUM QUANTITY REMARKS
15-MAY-1996 TRUE WHEEL 23 6 PAID
19-MAY-1996 TRUE WHEEL 76 3 PAID
2-SEP-1996 TRUE WHEEL 10 1 PAID
30-JUN-1996 TRUE WHEEL 42 8 PAID
30-JUN-1996 BIKE SPEC 54 10 PAID
30-MAY-1996 BIKE SPEC 10 2 PAID
30-MAY-1996 BIKE SPEC 23 8 PAID
17-JAN-1996 BIKE SPEC 76 11 PAID
17-JAN-1996 LE SHOPPE 76 5 PAID
1-JUN-1996 LE SHOPPE 10 3 PAID
1-JUN-1996 AAA BIKE 10 1 PAID
1-JUL-1996 AAA BIKE 76 4 PAID
1-JUL-1996 AAA BIKE 46 14 PAID
11-JUL-1996 JACKS BIKE 76 14 PAID
分析
两表的共有字段是PARTNUM 假如你不知道或者是不想知道这个字段但是你
又想用PART 表的description 字段来工作这时可以使用子查询语句如下
INPUT/OUTPUT
SELECT * FROM ORDERS WHERE PARTNUM =
(SELECT PARTNUM FROM PART WHERE DESCRIPTION LIKE "ROAD%")
ORDEREDON NAME PARTNUM QUANTITY REMARKS
19-MAY-1996 TRUE WHEEL 76 3 PAID
17-JAN-1996 BIKE SPEC 76 11 PAID
17-JAN-1996 LE SHOPPE 76 5 PAID
1-JUL-1996 AAA BIKE 76 4 PAID
11-JUL-1996 JACKS BIKE 76 14 PAID
分析
SQL 21 日自学通(V1.0) 翻译人笨猪
140
更进一步如果你使用了在第六天中的概念你可以使PARTNUM 列带有
DESCRIPTION 这样就会使那些对PARTNUM 还不太清楚的人看得更明白些如下例
INPUT/OUTPUT
SELECT O.ORDEREDON O.PARTNUM P.DESCRIPTION O.QUANTITY O.REMARKS
FROM ORDERS O PART P WHERE O.PARTNUM = P.PARTNUM
AND O.PARTNUM =(SELECT PARTNUM FROM PART
WHERE DESCRIPTION LIKE "ROAD%")
ORDEREDON PARTNUM DESCRIPTION QUANTITY REMARKS
19-MAY-1996 76 ROAD BIKE 3 PAID
1-JUL-1996 76 ROAD BIKE 4 PAID
17-JAN-1996 76 ROAD BIKE 5 PAID
17-JAN-1996 76 ROAD BIKE 11 PAID
11-JUL-1996 76 ROAD BIKE 14 PAID
分析
查询的第一部分非常熟悉
SELECT O.ORDEREDON O.PARTNUM P.DESCRIPTION O.QUANTITY
O.REMARKS FROM ORDERS O PART P
这里使用了别名O 和P 来指定了在ORDERS 和PART 表中你所感兴趣的5 列对于
你要访问的在两个表中的名字唯一的列别名是没有必要的可是它可以使你的语句更具有
可读性你看到的第一个WHERE 子句内容如下
WHERE O.PARTNUM = P.PARTNUM
它是将ORDERS 与PART 表进行归并的标准语句如果你没有使用WHERE 子句那
么你将会得到两个表的记录的所有可能的组合接下来就是子查询语句内容如下
AND O.PARTNUM =(SELECT PARTNUM FROM PART WHERE DESCRIPTION LIKE "ROAD%")
增加的限制使你的PARTNUM 内容必须与你的子查询所返回的结果相等子查询则非常简
单它要求返回以ROAD%相符的PARTNUM 使用LIKE 语句是一种懒人的办法使得
你不必键入ROAD BIKE 但是这只是你侥幸如果在PART 表中加入了一个新的记录名
字为ROADKILL 时呢这时PART 表的内容如下
INPUT/OUTPUT
SELECT * FROM PART
PARTNUM DESCRIPTION PRICE
SQL 21 日自学通(V1.0) 翻译人笨猪
141
PARTNUM DESCRIPTION PRICE
54 PEDALS 54.25
42 SEATS 24.50
46 TIRES 15.25
23 MOUNTAIN BIKE 350.45
76 ROAD BIKE 530.00
10 TANDEM 1200.00
77 ROADKILL 7.99
如果你没有觉察到这些改变而仍然使用原来的查询的话你将会得到如下信息
multiple rows in singleton select
你没有得到任何结果SQL 的响应信息可能不会相同但是你都会同样地得不到任何结果
想知道为什么会有这样的结果请想一个SQL 引擎的处理规则你需要重新核查一下你的
子查询请输入
INPUT/OUTPUT
SELECT PARTNUM FROM PART WHERE DESCRIPTION LIKE "ROAD%"
PARTNUM
76
77
你会把这个结果赋给O.PARTNUM = 就是这一步导致的错误
分析
PARTNUM 怎么能同时匹配76 和77 呢解释器一定会给你这样的信息的因为你是
一个懒家伙当你使用LIKE 子句的时候你就已经开始了犯错误的道路如果你想使用
比较运算符如> <和=时你必须确保你的查询结果是唯一的在我们现在的这个例子中
应该使用=号来代替LIKE 如下
INPUT/OUTPUT
SELECT O.ORDEREDON O.PARTNUM P.DESCRIPTION O.QUANTITY
O.REMARKS FROM ORDERS O PART P WHERE O.PARTNUM = P.PARTNUM
AND O.PARTNUM = (SELECT PARTNUM FROM PART
WHERE DESCRIPTION = "ROAD BIKE")
ORDEREDON PARTNUM DESCRIPTION QUANTITY REMARKS
19-MAY-1996 76 ROAD BIKE 3 PAID
1-JUL-1996 76 ROAD BIKE 4 PAID
SQL 21 日自学通(V1.0) 翻译人笨猪
142
ORDEREDON PARTNUM DESCRIPTION QUANTITY REMARKS
17-JAN-1996 76 ROAD BIKE 5 PAID
17-JAN-1996 76 ROAD BIKE 11 PAID
11-JUL-1996 76 ROAD BIKE 14 PAID
分析
这个子查询将会返回唯一的结果因为使用=会返回唯一的结果当你需要唯一的结
果时如何才能避免子查询返回多个结果呢
首先是不要使用LIKE 再就是设计表的时候就要保证你要搜索的字段内容是唯一的
你可以使用表自我归并的方法昨天讲过来检查给定字段的内容是否是唯一的如果表
是你自己设计的你要让你搜索的列是表中的唯一列你也可以使用SQL 只返回单一结果
的部分— — 汇总函数
gototop
 

在子查询中使用汇总函数
像SUM AVG COUNT MIN 和MAX 等汇总函数均返回单一的数值如果想知道
定单的平均金额可以用如下语句
INPUT
SELECT AVG(O.QUANTITY * P.PRICE)
FROM ORDERS O, PART P
WHERE O.PARTNUM = P.PARTNUM
OUTPUT
AVG
2419.16
分析
这条语句只返回一个平均值如果你想找一下都有哪些定单的金额高于平均值的话
可以将上述语句使用子查询使用完整的语句内容如下
INPUT/OUTPUT
SELECT O.NAME O.ORDEREDON O.QUANTITY * P.PRICE TOTAL
FROM ORDERS O PART P WHERE O.PARTNUM = P.PARTNUM AND
O.QUANTITY * P.PRICE> (SELECT AVG(O.QUANTITY * P.PRICE)
FROM ORDERS O PART P WHERE O.PARTNUM = P.PARTNUM)
SQL 21 日自学通(V1.0) 翻译人笨猪
143
NAME ORDEREDON TOTAL
LE SHOPPE 1-JUN-1996 3600.00
BIKE SPEC 30-MAY-1996 2803.60
LE SHOPPE 17-JAN-1996 2650.00
BIKE SPEC 17-JAN-1996 5830.00
JACKS BIKE 11-JUL-1996 7420.00
分析
在这个例子中的SELECT/FROM/WHERE 子句的区别不太明显
SELECT O.NAME O.ORDEREDON O.QUANTITY * P.PRICE TOTAL
FROM ORDERS O PART P WHERE O.PARTNUM = P.PARTNUM
这是归并两个表的常用方法这种归并是必须的因为单价在PART 表上而数量则在
ORDERS 表上WHERE 子句用来检测相关性错误指一个主关键字对应两条记录的情况
之后则是子查询语句
AND
O.QUANTITY * P.PRICE>(SELECT AVG(O.QUANTITY * P.PRICE)
FROM ORDERS O PART P WHERE O.PARTNUM = P.PARTNUM)
第一个比较表达式是将每一条记录的金额与子查询中的平均金额进行比较注意子查
询中使用归并的原因与主查询是相同的它严格地遵循着归并的语法在子查询中没有什
么秘密可言它与单独的查询具有相同的语法格式事实上大多数子查询都是作为独立
查询经过测试确定其只返回一个值以后才作为子查询使用的
子查询的嵌套
嵌套就是将一个子查询嵌入到另一个子查询中去例如
Select * FROM SOMETHING WHERE ( SUBQUERY(SUBQUERY(SUBQUERY)))
子查询可被嵌套的深度依你的需要而定例如如果你想给那些花费超过了平均价格
的客户发一个特别通知你将会使用CUSTOMERS 表中的如下信息
INPUT
SELECT * FROM CUSTOMER
OUTPUT
SQL 21 日自学通(V1.0) 翻译人笨猪
144
NAME ADDRESS STATE ZIP PHONE REMARKS
TRUE WHEEL 55O HUSKER NE 58702 555-4545 NONE
BIKE SPEC CPT SHRIVE LA 45678 555-1234 NONE
LE SHOPPE HOMETOWN KS 54678 555-1278 NONE
AAA BIKE 10 OLDTOWN NE 56784 555-3421 JOHN-MGR
JACKS BIKE 24 EGLIN FL 34567 555-2314 NONE
你只需要对上边你的查找定单的查询做一点改动即可
INPUT/OUTPUT
SELECT ALL C.NAME C.ADDRESS C.STATE C.ZIP FROM CUSTOMER C
WHERE C.NAME IN
(SELECT O.NAME FROM ORDERS O PART P
WHERE O.PARTNUM = P.PARTNUM
AND
O.QUANTITY * P.PRICE> (SELECT AVG(O.QUANTITY * P.PRICE)
FROM ORDERS O PART P
WHERE O.PARTNUM = P.PARTNUM))
NAME ADDRESS STATE ZIP
BIKE SPEC CPTSHRIVE LA 45678
LE SHOPPE HOMETOWN KS 54678
JACKS BIKE 24EGLIN FL 34567
分析
注意一下圆括号最里边的内容你会发现类似的语句
SELECT AVG(O.QUANTITY * P.PRICE)
FROM ORDERS O PART P WHERE O.PARTNUM = P.PARTNUM
结果传入的语句与你以前使用的SELECT 语句有一些不同之处
SELECT O.NAME FROM ORDERS O PART P WHERE O.PARTNUM = P.PARTNUM
AND O.QUANTITY * P.PRICE>(...)
注意SELECT 子句已经被改为返回单一的NAME 列运行该查询你会得到下表
NAME
LE SHOPPE
BIKE SPEC
LE SHOPPE
SQL 21 日自学通(V1.0) 翻译人笨猪
145
NAME
BIKE SPEC
JACKS BIKE
我们曾经花过一些时间来讨论为什么子查询应该只返回一个数值而这个查询返回
了多个数值则是显而易见的
将上述结果引入下边的语句
SELECT C.NAME C.ADDRESS C.STATE C.ZIP FROM CUSTOMER C
WHERE C.NAME IN (...)
分析
头两行没有什么特别的内容在第三行时再次引入了关键字IN 看一下第二天的查
询简介SELECT 语句的使用IN 是一种允许你在子查询中进行多行输出的工具就像
你原来记得的那样它将返回与所列内容相匹配的记录它的列出内容如下
LE SHOPPE
BIKE SPEC
LE SHOPPE
BIKE SPEC
JACKS BIKE
根据子查询的条件得到了下边的内容
NAME ADDRESS STATE ZIP
BIKE SPEC CPT SHRIVE LA 45678
LE SHOPPE HOMETOWN KS 54678
JACKS BIKE 24 EGLIN FL 34567
在子查询中使用关键字IN 是非常普遍的因为IN 可以与一组数据进行对比而且它
不会使SQL 的引擎检查出其中有冲突或是不合适的地方
子查询也可以使用GROUP BY 和HAVING 子句见下例
INPUT/OUTPUT
SELECT NAME AVG QUANTITY FROM ORDERS
GROUP BY NAME HAVING AVG QUANTITY > SELECT AVG QUANTITY
FROM ORDERS
NAME AVG
BIKE SPEC 8
JACKS BIKE 14
SQL 21 日自学通(V1.0) 翻译人笨猪
146
分析
让我们来看一下这个查询在引擎中的工作过程首先请看子查询
INPUT/OUTPUT
SELECT AVG QUANTITY FROM ORDERS
该查询返回的结果为6
而主查询的结果如下
INPUT/OUTPUT
SELECT NAME AVG QUANTITY FROM ORDERS GROUP BY NAME
NAME AVG
AAA BIKE 6
BIKE SPEC 8
JACKS BIKE 14
LE SHOPPE 4
TRUE WHEEL 5
在经过HAVING 子句的检查后该查询给出了两条大于平均QUANTITY的记录
INPUT/OUTPUT
HAVING AVG QUANTITY > SELECT AVG QUANTITY FROM ORDERS
NAME AVG
BIKE SPEC 8
JACKS BIKE 14
gototop
 
1234   3  /  4  页   跳转
页面顶部
Powered by Discuz!NT