瑞星卡卡安全论坛
斧子 - 2006-2-11 18:00:00
第一周概貌
从这里开始
在本周我们将向大家介绍SQL 的发展历程及其前景并来学习第一个SQL 语句— —
SELECT 语句它使我们能够用自己的方法来从数据库中检索到自己想要的数据同时
在第一周我们也将学习SQL 的函数联合查询及子查询嵌于查询中的查询并举出多
个例子以帮助您理解它们这些例子是适用于Oracle7 Sybase SQL Server Microsoft
Access Microsoft Query 我们会用高亮显示指出它们的相似之处以及不同点读者们会
觉得这些例子更具有适用性和趣味性
斧子 - 2006-2-11 18:03:00
第一天SQL 简介
SQL 简史
SQL 的诞生于IBM 公司在加利福尼亚San Jose 的试验室中在七十年代SQL 由这里
开发出来最初它们被称为结构化查询语言Structured Query Language 并常常简称为
sequel 开始时它们是为IBM 公司的DB2 系列数据管理系统RDBMS — — 关系型数据库
管理系统而开发的您在今天仍可以买到在不同平台下运行的该系统事实上是SQL
造就了RDBMS 它是一种非过程语言与第三代过程语言如C 和COBOL 产生于同一时
代
注非过程性语言的意思就是指与具体过程无关举例来说SQL 描述了如何对数据
进行检索插入删除但它并不说明如何进行这样的操作
这种特性将RDBMS 从DBMS 中区别开来RDBMS 提供了一整套的针对数据库的语
言而且对于大多数的RDBMS 来说这一整套的数据语言就是SQL 这里一整套的意思
就是对数据和处理操作语言是一些过程的集合
有两个标准化组织美国国家标准协会ANSI 和国际标准组织ISO 正致力于SQL
在工业领域的标准化应用工作本书使用的标准为ANSI-92 尽管该标准要求所有的数据
库设计者应遵守这一标准然而所有的数据库系统所用的SQL 均与ANSI-92 存在一定的
差异此外大多数数据库系统对SQL 进行了有针对性的扩展使它们成为了过程型语言
在本书中我们对不同的RDBMS 系统给出了它们的SQL 语言例句希望你能从中发现它们
的共性我们将要讨论的过程型SQL 有PL/SQL 和Transact-SQL 它们将在第18 天和第
19 天提到
数据库简史
对数据库的发展历程有一个简要的了解可以使您更清楚如何使用SQL 来工作数据库
系统在商业领域应用极为广泛大到航空机票售票系统小到孩子们的棒球卡管理系统
数据库将按照我们的意愿来存储和处理这些数据直到最近几年以前大型的数据库系统
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 20
仍只能在大型机上运行而大型机的运行维护使用费用均是非常昂贵的然而在今天
工作站的能力强大到可以让编程人员以极快的速度和极低的价格来设计和发布软件
Dr. Codd's 对关系型数据库系统的十二条规则
关系型数据库是最为流行的数据存储模式它产生于一个名称为A Relational Model of
Data for Large Shared Data Banks 的论文中SQL 进而发展为关系型的数据库对于关系
型数据库Dr. Codd's 定义了12 条规则使之与其他类型的数据库相区别
0 关系型数据库必须通过关系来实现对数据的完全管理
1 所有在关系型数据库中的信息均可以在表中以数值的形式加以体现
2 在关系型数据库中的每一项数据均可以通过库名键名和列名来准确指定
3 关系型数据库系统必须对空值未知的和违规的数据提供系统级的支持有独特
的缺省值而且具有独立域{这一段不太清楚}
4 活动的即时的数据联合— — 它的意思就是在数据库中的数据应有逻辑表格的行的
形式来表达并且可以通过数据处理语言来访问
5 完善的数据子语句— — 它应该至少支持一种有严格语法规则和功能完善的语言并
且应该支持数据和定义处理完整性权限以及事务等操作
6 查看更新规则— — 所有在理论上可以更新的视图可以通过系统操作来更新
7 数据库中数据和插入更新与删除操作— — 该数据库系统不仅要支持数据行的访
问还要支持数据和的插入更新和删除操作
8 数据和物理独立性— — 当数据在物理存储结构上发生变化时应用程序在逻辑上不应
受到影响
9 数据的逻辑独立性— — 当改变表的结构时应用程序在最大程度上不受影响
10 有效性独立— — 数据库的语言必须有定义数据完整性规则的能力数据应即时存
储在线目录而且在处理时必须通过这一五一节
11 发布的独立性— — 当数据第一次发布或当它重新发布时应用程序应不受影响
12 任何程序不可能使用更低级的语言从而绕过数据库语言的有效性规则定义
大多数数据库具有父/子关系这就是说在父结点中保存有子结点的文件指针见下
图
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 21
这种方式有优点也有缺点它的好处在于它使得数据在磁盘上的物理存储结构变得不
再重要编程人员只需存储下一个文件的指针就可以实现对下一个文件的访问而且数据
的添加和删除操作也变得非常容易可是不同组的信息想要联合为一个新组就变得困难了
这是因为在这种方式下数据在磁盘上的存储格式不能在数据库建立以后再强制性地改变
如果需要这样做那就必须重新建立一个数据库结构
Codd's 的关系型数据库思想借用的逻辑代数的思想使得数据的子集与父级之间具有
平等的地位
由于信息可以很自然地组织在不同的表格中Dr. Codd 也以这种方式来组织他所提出
的数据库在关系模式下数据被存入类似于表格的结构中这种表格由独立的数据元组
被称为列或字段所组合而成一组数据信息被存储为一行举例来说创建一个包括
雇员内容的关系型数据库我们可以很容易地从雇员表开始而像这样的表在很容易得到
的该表中包含有如下信息姓名年龄职业这三项数据用作雇员表的字段整个表
如下图所示
姓名年龄职业
Will Williams 25 Electrical Engineer
Dave Davidson 34 Museum Curator
Jan Janis 42 Chef
Bill Jackson 19 Student
Don DeMarco 32 Game programmer
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 22
Becky Boudreaux 25 Model
在这个表中有六行记录为了从中找到特定的记录举例来说Dave Davidson 用
户可以望知数据库管理系统在数据库中检索满足条件姓名= Dave Davidson 的记录
如果数据库管理系统已经检索过了全部的数据那么它将会把满足条件的的姓名年龄
职业三项的记录返回给用户SQL 会通知DBMS 找什么样的数据这一检索过程的SQL
例句如下
SELECT * FROM EMPLOYEE
在这里不要刻意去记它的语句我们在明天将会对它进行更为详细的讨论
由于通过明显的关系可以特不同的数据项归结在一起比如雇员的姓名和雇员的年
龄所以关系性数据库管理系统对如何来描述数据之间的关系给出了相当大的弹性通过
精确的的连接和联合运算关系型数据库管理系统可以非常迅速地从不同的表中将所需要
的数据联合见联合运算图然后返回给用户或程序这种联合的特性允许数据库的设计
者将数据信息存储在不同的表中以减少数据的冗余度
右图则反映了相交运算相交运算地意思就是取出两个或多个库所共有的部分
这里有一个简单的例子来显示数据是如何进行逻辑处理的表1-2 是一个被称为报
告的表它的里边有两个字段姓名和工作
Name Duties
Becky Boudreaux Smile
Becky Boudreaux Walk
Bill Jackson Study
Bill Jackson Interview for jobs
在雇员数据库中的年龄和职业字段在每一个记录中均出现重复是不合适的而且随着
时间的进行这些冗余的数据将会占用大量的磁盘空间且使得数据库管理系统在检索数据
表A 表B
联合运算
表A 表B
相交运算
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 23
所耗用的时间增多可是如果你将姓名和工作另存到一个名字叫报告的库中以后
你就可以通过联合操作将报告与雇员通过姓名字段进行联合操作也就是通知RDBMS
将报告与雇员库中姓名与Becky Boudreaux 相同的记录显示出来其结果将如下
所示
Name Age Occupation Duties
Becky Boudreaux 25 Model Smile
Becky Boudreaux 25 Model Walk
关于联合运算的详细讲述将在第6 天的库的联合中讲述
设计数据库的结构
在数据库的设计师已经决定了系统的硬件平台和RDBMS 系统以后余下事情中最为
重要的就是如何来设计数据库的结构了数据库的结构将会影响到是后运行于该库上的应
用程序的性能这个决定数据库的分配情况及联合运算的过程称之为标准化
数据库的前景
电脑技术将对今天世办上的商业产生深远的影响鼠标只要点一下就可以将数据入库
或对其进行访问制造商的国外订货单可以立即接受并执行尽管在20 年以前信息的交换
还需要大型机的支持而办公领域处理事务也仍在采用批处理的方式要完成某一个查询
用户需要将需求提交给服务器上的信息管理系统MIS 给果将会以最快的速度返回给用
户尽管经常不是足够快
此外随着关系型数据库模型的发展有两种技术被引用到了在今天被称为服务器/客
户机的数据库系统当中第一项技术就是个人电脑廉价而又易用的应用程序如Lotus1-2-
3 和WordPerfect 允许员工或家庭用户可以建立文档来快速而准确地处理数据用户也
会经常升级他们的系统以使其速度更快巧的是这时的系统的价格却在迅速下跌
第二项技术则是局域网的发展它导致的世界范围内的办公交叉— — 虽然用户习惯于
采用终端同主机相连在今天一个字处理文档可以存储在本地而被任何连接到网络上的
电脑访问然后苹果的Macintosh 电脑为大家提供了一个友好易用的图形用户界面使得
电脑变得物美价廉此外他们可以访问远程站点并从服务器上下传大量的数据
在这个飞速发展的时期一种新型的叫作服务器/客户机的系统诞生了这种系统的处
理过程被分解上了客户机和数据服务器上新型的应用程序取代了基于主机的应用程序
这一体系有着相当多的优点
l 降低了维护费用
l 减轻的网路负荷处理过程在服务器上和客户机上均有
l 多个操作系统可以基于相同了网络协议来共同工作
l 本地化的数据操作提高了数据的完整性
对于什么是客户机/服务器型电脑系统Bernard H. Boar 的定义如下
客房机/服务器系统就是把单一的任务分解到多个处理器上进行协同处理,就像在单个
处理器上运行时一样一个完备的客户机/服务器系统可以将多个处理器捆绑在一起
以提供一个单一系统虚拟环境共享的资源可以被位于远端的客户机通过特殊的服
务来访问这种结构可以逐级递归所以一级服务器可以在最后转变为客户机进需
要求其他的服务器提供服务就这样一直下去
这种类型的应用程序在设计时需要全新的程序设计技巧今天的用户界面都是图形用
户界面不论是微软的WINDOWS 苹果的MACINTOSH IBM 的OS/2 还是UNIX 的Xwindows
系统均是如此用过使用SQL 和网络应用程序就可以访问位于远端服务器上的
数据库个人电脑处理能力的提高可以对存放在一系统相关的服务器的数据库作出评定
而这此服务器是可以更换的而应用程序则只需做出较少的改动甚至无需改动
交互式语言
本书在许多场合下可以借用BASIC 的概念举例来说Microsoft Access 是基于windows
的单用房应用程序而SQL SEVER 则可以允许100 个用户同时工作SQL 的最大优越性在
于它是一种真正的跨平台的交互式语言由于它可以被程序员在第四代的编程语言中调用
第四代编程语言可以做用少量的代码做大量的工作
易于实现
ORACLE 公司是第一个发行基本于SQL 的关系型数据库管理系统RDBMS 的公司
虽然它是为VAX/VMS 系统开发的ORACLE 公司也是DOS 下的关系型数据库的供应商
之一ORACLE 现在可以运行在近70 种平台之上在八十年代中期Sybase 公司发行了
他们的RDBMS — — SQL Sever 具有客户端数据库访问功能并支持过程存储将在第14 天
的动态应用SQL 中提到和多平台交互工作能力SQL Sever 作为一个成功的产品其客
户机/服务器工作能力是其最为突出的优势所在这个强大的数据库系统具有极高的平台适
应性用C 语言写成的运行于PC 机的ORACLE 其实是运行于VAX 系统上的ORACLE 的
复制
SQL 与客户机/服务器应用程序开发环境
在使用客户机/服务器电脑来开发客户机/服务器应用程序时SQL 和关系型数据库的思
想遍及始终在单用户系统中使用这种技术也可以使您的程序更适应未来的发展
SQL 总览
SQL 是操作和检索关系型数据库的事实上的标准语言它允许程序员和数据库管理员
做如下的工作
l 更改数据库的结构
l 更改系统的安全设置
l 增加用户对数据库或表的许可权限
l 在数据库中检索需要的信息
l 对数据库的信息进行更新
注对于SQL 大家可能还不明白S 即Structured 结构L 即Language 语言这是显
而易见的但是Q 的意思容易让人误解Q 的意思当然是Query 查询— — 如果你
直译的话可是这只限于你对数据库提问但是SQL 能干的不只是查询通过它你可
以建立一个库添加和删除数据对数据作联合当数据库改变时触发动作并把你
的查询存储在程序或数据库中
不幸得很这对于查询来说似乎是一个缺点显然库结构的增加删除修改联
合存储触发以及查询语言在多用户协同工作时有点烦琐我们将会在工作中一直与SQL
打交道不过现在你应该知道它的功能不只是限于它的名字所指的内容了
斧子 - 2006-2-11 18:03:00
SELECT 语句是SQL 中应用最多的语句见第二章查询— —SELECT 语句的使用
它会从数据库中检索需要的数据并把结果返回给用户上边的雇员表的举例便是一个典型
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 26
的SELECT 语句的使用例子除了SELECT 语句之外SQL 还提供了用以建立新的数据库
表格字段和索引的语句以及记录的插入和删除语句你将会发现ANSI SQL 还提供了
对核心数据操作的功能许多数据库管理系统还提供了确保数据完整性和强制安全性的工
具见第11 天的传输控制它允许程序员在当前环境不符合的时候强制性地终止语句组
的执行
流行的SQL 开发工具
这一部分将介绍一些大众化的SQL 开发工具每一种工具都有它的优点和缺点一些
工具是基于PC 用户的强调易用性而另一些则是为超大型数据库提供的本部分将向
您介绍选择这些工具的关键所在
注为了SQL 在实际中的使用本书中举出了一些SQL 在实际开发环境中的应用例
子SQL 只有出现在你的代码中为你真正地解决了问题才说明它是有用的
Microsoft Access
在一些应用实例中我们将会举一些Microsoft Access 的例子Microsoft Access 是一个
非常容易使用的基于PC 机的数据库管理系统在它的下边你既可以手工输入SQL 语句也
可以使用图形用户界面工具来生成SQL 语句
Personal Oracle7
我们使用Personal Oracle7 来向大家演示SQL 对大型数据库上的命令行使用方法当
用户对一个数据库或一个操作系统有充分的了解以后需要在一个孤立的电脑上进行设计
时这种方法是非常重要的在命令行下用户可以在SQL PLUS 工具中输入不同的单的SQL
语句该工具可以把数据返回给用户或是对数据库进行适当的操作
大多数例子是针对初用SQL 进行程序设计的程序员的我们从最简单的SQL 语句开
始并进阶到事务处理阶段为程序设计做好准备Oracle 的发行版提供一整套的开发工具
它包括C++和Visual Basic 函数库(Oracle Objects for OLE) 通过它可以将应用程序与
ORACLE 个人数据库链接在一起它也可以为数据库用户或管理员提供图形工具同
SQL*Loader 一样它也经常用于从Oracle 数据库中导出或引入数据
注Personal Oracle7 是Oracle7 server 的不完整版它只允许单用户操作就如同它的名字
一样但它在SQL 的语法使用与大型更昂贵的Oracle 版本是相同的此外在Personal
Oracle7 中所使用的工具也适用于Oracle 的其他版本
我们选择Personal7 基于以下原因
l 它几乎用到了本书中将要讨论的所有工具
l 它是可以在几乎全部的平台运行的风靡世界的关系型数据库管理系统
l 可以从Oracle 公司的服务器上下载一个它的90 天限时版(http://www.oracle.com).
上图显示了SQL-PLUS 的一个画面
技巧注意在本书中所给出的所有SQL 代码都适用于其它的数据库管理系统防止在其它
的系统中语法出现大的差别在本书的例子中指出了它在其它系统中的不同之处
Microsoft Query
Microsoft Query 是Microsoft 公司的Visual C++和Visual Basic 开发工具包中所附带的
一个非常有用的查询工具它可应用在基于ODBC 标准下的数据库该查询工具在将查询
语句提交给数据库之前会将基保留在驱动器上
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 28
开放型数据库联接ODBC
ODBC 是为应用程序接口API 提供的访问下层数据库所
用的函数库它通过数据库引擎与数据库交流就像在Windows
通过打印驱动程序与打印机通信那样为了访问数据库可能还
会需要网络驱动程序与异地数据库通信ODBC 的结构如右图所
示
ODBC 的特色在于它不是针对任何一种数据库的举例来说
你可以用相同的代码来在Microsoft Access 表中或Informix
database 上运行查询而无需修改代码或只需做很小的改动再
提醒您一次第三方数据库供应商可能会在SQL 的标准之上对其
进行扩充比如Microsof 和Sybase 的Transact-SQL 以及Oracle
的PL/SQL
当您用一种新型的数据库工作时您应该认真阅读一下它的文档ODBC 是许多数据库
所支持的一种标准包括Visual Basic Visual C++ FoxPro Borland Delphi 和PowerBuilder
基于ODBC 所开发的应用程序有着明显的优势因为它允许你在编写代码的时候不必考虑
是为哪一个数据库所写的当然它的运行速度要弱于特定的数据库代码也就是说在使
用ODBC 的时候其灵活性更强但比起使用Oracle7 或Sybase 的函数库时要慢
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 29
SQL 在编程中的应用
SQL 的最初标准是ANSI-1986 而在ANSI-1989 中定义的SQL 在应用程序中的三种
接口类型
l 模块语句— — 在程序中使用过程该过程可以通过主调参数向主调函数返回值
l 内嵌SQL — — 可以在编写程序的过程中内嵌SQL 语句该方式在经常需要对SQL
语句进行预编译处理时所需要在Pascal FORTRAN COBOL PL/1 中均定义
了这样的子句
l 直接调用— — 由程序直接实现
在动态SQL 发展以前内嵌SQL 在编程中应用最为流行这种方法在今天也仍然在
使用由于静态的SQL — — 它的意思就是SQL 语句已经被编译在了应用程序之中不能在
运行的过程中改变这与编译程序同解释程序的区别类似也就是说这种类型的SQL 速度
很快但是灵活性很差这在今天的商业应用领域是不适宜的动态SQL 这里就不多说了
ANSI-92 标准将SQL 语言标准扩展为一种国际化的标准它定义了SQL 的三种编译
级别登录调用内嵌子句和完全编译主要的新特性如下
l 联接到数据库
l 移动游标
l 动态SQL
l 外连接
本书除了这些扩展以外还包括了第三方数据库供应商所提供的扩展动态SQL 允许你
在运行时修改SQL 语句但是它的速度要比内嵌型SQL 慢它在调用级接口上为应用程
序开发人员提供了相当大的灵活性ODBC 或Sybase 的DB-Library 就是动态SQL 的例子
调用级接口对于编程人员来说不是一个新概念当您在使用ODBC 时举例来说你
需要在SQL 语句提交给数据库时修改子句中的变量参数而使用该功能在设计阶段可以通
过使用其它的函数调用接收错误信息或结果结果是以数据包的形式返回的
摘要
在第一天介绍了SQL 的历史由于SQL 与关系型数据库是紧密结合的所以第一天也简
要地介绍了关系型数据库的历史和它的功能明天我们将讲述SQL 中最为常用的功能— —
查询
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 30
问与答
问为什么我要了解SQL
答到现在为止如果你不知道如何利用大型数据库来进行工作如果你要使用客户机/服
务器型应用程序开发平台如Visual Basic Visual C++ ODBC Delphi 和
PowerBuilder 以及一些已经移植到PC 平台上的大型数据库系统如Oracle 和
Sybase 来进行开发工作那么你只有学习SQL 的知识在今天大多数的商用程序开
发都需要你了解SQL
问在学习SQL 时我为什么要了解关系型数据库系统
答SQL 在为关系型数据库系统开发的不知道点关系型数据库系统的知识你就无法有效
地使用SQL
问在GUI 图形用户界面工具下我只需按按钮就可以写出SQL 语句为什么我还需要学
习手工写SQL
答GUI 有GUI 的方法手工有手工的方法一般说来手工写出来的SQL 比GUI 写出来
的更有效率GUI 的SQL 语句没有手工写出的易读而且如果你知道如何用手工来写
SQL 的话那么你在使用GUI 时就会有更高的效率
问既然SQL 是一种标准化语言那是不是说我可以用它在任何数据库下进行编程
答不是你只能将它用于支持SQL 的关系型数据库系统如MS-Access Oracle Sybase
和Informix 尽管不同的系统在执行时有所差别但是你只需要对你的SQL 语句进行
很小的调整
校练场
在校练场里我们提出了一些问题以帮助你巩固自己所学这些练习可以提高你在学习中
的经验请试着回答和练习附录五问答与练习中的内容
1 为什么说SQL 是一种非过程型语言
2 我如何知道一种数据库系统是不艺机是关系型数据库系统
3 我可以用SQL 来做什么
4 把数据清楚地分成一个个唯一集的过程叫什么名字
练习
确认一下你所使用的数据库系统是否是一个关系型数据库系统
斧子 - 2006-2-11 18:04:00
第二天查询— — SELECT 语句的使用
目标
欢迎来到第二天在今天你将学习到以下内容
l 如何写SQL 的查询
l 将表中所有的行选择和列出
l 选择和列出表中的选定列
l 选择和列出多个表中的选定列
背景
在第一天中我们简要地介绍了关系型数据库系统所具有的强大功能在对SQL 进行了
简要的介绍中我们知道了如何同它进行交流最终我们将会与计算机用一种非常清楚
果断的话说给我看一下所有在本公司中工作十年以上左撇子蓝眼睛的外国人如
果你能够这样做与计算机交流而不是查他们的档案每一个人都可以用他自己的方法
来达到目的但是你却是用SQL 的一种重要功能— — 查询来达到目的
在第一天中我们说过查询一词用在SQL 中并不是很恰当在SQL 中查询除了向数
据库提出问题之外还可以实现下面的功能
l 建立或删除一个表
l 插入修改或删除一个行或列
l 用一个特定的命令从几个表中查找所需要的信息并返回
l 改变信息的安全性
SQL 的查询当然也能进行一般的查询工作在学会使用这个有用的工具之前我们来
学习如何写SQL 的查询语句
一般的语法规则
正如你所看到的那样SQL 有很高的灵活性尽管在任何程序中都有一定的规则限制
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 33
下而有一个SQL 中SELECT 语句使用的简单例子请注意在每个SQL 语句的关键字都
是大写的并且用空格将他们划分出来
SELECT NAME STARTTERM ENDTERM
FROM PRESIDENTS
WHERE NAME LINCOLN
在这个例子中每一个字母都是大写的但是这不是必需的上边的查询语句完全可以
写成这样
select name startterm endterm
from presidents
where name LINCOLN
注意LINCOLN 在这里仍然是大写的尽管SQL 语句对大小写并不敏感但在数据
库中的数据却是大小写敏感的举例来说许多公司在储存数据时用大写字母在这种情
况下所有的字段名也将是大写字母那么在检索条件为name='Lincoln'的数据时将不会得
到任何结果这种情况在每个实例应用中都会遇到
注意在SQL 语句中大小写是不敏感的
现在我们来看另一个例子在这个例子中的空格有问题吗不是这个语句完全可以
正常执行
Select name startterm endterm from presidents where name='LINCOLN'
但是如果你注意在你的语句中使用空格和大写字母会增强语句的可读性当它变成
你的工程编程的一部分时会更便于维护
另一个重要的特性是分号当在SQL 语句中出现分号就意味着本条语句已经结束
为什么在格式中大小写是不重要的原因何在答案是— — 关键字关键字是SQL 语
法中的保留字在SQL 语句中关键字是可选择的但其内容有强制性在本例中的关键
字有
SELECT
FORM
WHERE
看一下目录你会找到需要在其它几天中学习的关键字
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 34
数据报的形成— — SELECT 和FROM
随着对SQL 的了解你会发现你键入的SELCT 和FROM 在远远多于其它的关键字
它不像CREATE 那样迷人或像DROP 那样残忍但是如果你在同计算机会话并需要计算机
返回结果时它们却是必不可少的这与最初选择何种数据库没有关系
我们先从SELECT 开始讨论因为SELECT 是在SQL 中使用最为频繁的语句
语法
SELECT <列名>
没有其它的语句可以比SELECT 语句更简单了但是SELECT 语句不从独立工作如
果你只是键入了SELECT 语句那么你将会收到如下信息
输入
SQL> SELECT;
输出
SELECT
*
ERROR at line 1
ORA-00936 missing expression
当在访问ORACLE 时会有*出现以表示有事件产生错误信息的意思是告诉你有一个
东西丢了这个丢失的东西就是FROM 子句
语法
FROM <表名>
当两条语句结合使用时就有了后台访问数据库的能力
注你可能会对子句关键字或SQL 语句感到费解SQL 的关键字是SQL 中的特定元
素如SELECT 和FROM 子句则是SQL语句的一部分如, SELECT column1, column2, ...
就是一个子句而SQL 语句则是几个子句的结合例如你可以将SELECT 子句和FROM
子句组合成为一个SQL 语句
注每一个种SQL 都有其特定的出错信息例如Microsoft Query 会显示说它不能运行查
询并引导你发现错误所在Borland's Interbase 将会弹出一个错误对话框Personal
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 35
Oracle7 的引擎将会出现如前所述的信息并给出一个错误号码所以当你手工输入SQL
语句时会看到详细的错误信息以及对错误的简要诠释
例子
在进一步学习之前我们先来看一个将要在下面的例子中用到的数据库这个数据库
体现了SELECT 和FROM 的基本功能在实际应用时我们将会用到在第8 天熟练地操作
数据中讲到的技巧来构建这个数据库但是我们现在的目的是学习如何使用SELECT 和
FROM 所以我们假设数据库已经建好了本例中使用CHECKS 表这个表的内容如下
CHECK# PAYEE AMOUNT REMARKS
1 MaBell 150 Havesonsnexttime
2 ReadingR.R. 245.34 TraintoChicago
3 MaBell 200.32 CellularPhone
4 LocalUtilities 98 Gas
5 JoesStale$Dent 150 Groceries
6 Cash 25 WildNightOut
7 JoansGas 25.1 Gas
斧子 - 2006-2-11 18:04:00
你的第一个查询
输入
SQL>select * from checks
输出
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
6 Cash 25 Wild Night Out
7 Joans Gas 25.1 Gas
7 rows selected.
分析
请看例子的输出结果注意第1 列和第3 列是右对齐的而第2 列和第4 列是左对齐的
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 36
这是因为对于数字类型采用右对齐而对于字符类型则是采用左对齐的数据的类型将在第
9 天的表的建立与维护中讨论
在SELECT 中的*表示要返回FROM 中所指定的表中的所有列并按照数据库中的固
有顺序来排序
完成一个SQL 语句
在SQL 运行时分号即意味着通知解释程序当前语句已经结束例如SQL*PLUS
在没有遇到分号时将不会执行语句但是在其它的SQL 解释器中可能不会用到分号例如
Microsoft Query 和Borland's ISQL 不需要查询终止符因为你是在编辑框中输入查询语句
并且当你在按下按钮以后才开始执行查询
对列进行排序
在前边的例子中使用了*来选择了选定表格中的所有列并且是按照其在数据库中的固
定顺序来排序的如果需要对特定的列排序你应该按下边所写的那样输入
输入
SQL> SELECT payee remarks amount check# from checks;
注意在SELECT 子句中给出了每个列的名字排序是根据列的先后顺序来进行的注
意将最后列的列名与其后的子句这里是FROM 用空格分开输出的结果如下
输出
PAYEE REMARKS AMOUNT CHECK#
Ma Bell Have sons next time 150 1
Reading R.R. Train to Chicago 245.34 2
Ma Bell Cellular Phone 200.32 3
Local Utilities Gas 98 4
Joes Stale $ Dent Groceries 150 5
Cash Wild Night Out 25 6
Joans Gas Gas 25.1 7
7 rows selected.
这句话也可以写成下边的形式
输入
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 37
SELECT payee, remarks, amount, check#
FROM checks;
注意这里的FROM 子句已经写到第二行了这是一种个人习惯其输出的结果如下
输出
PAYEE REMARKS AMOUNT CHECK#
Ma Bell Have sons next time 150 1
Reading R.R. Train to Chicago 245.34 2
Ma Bell Cellular Phone 200.32 3
Local Utilities Gas 98 4
Joes Stale $ Dent Groceries 150 5
Cash Wild Night Out 25 6
Joans Gas Gas 25.1 7
7 rows selected.
分析
语句的格式变更不会对输出的结果造成影响现在你已经知道了如何对输出的结果进
行排序试着将表格的列按照你的要求进行排序
选择特定的列
如果你不想看到数据库中的每一列当然你使用SELECT *将所有的列显示出是可
行的但是如果你只想看一下CHECKS 中的号码与数量列那么你可以输入如下语句
输入
SQL> SELECT CHECK#, amount from checks;
输出
CHECK# AMOUNT
1 150
2 245.34
3 200.32
4 98
5 150
6 25
7 25.1
现在你可以按要求显求所需要的列注意大小写的使用它不会对查询的结果造成影
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 38
响
如何从不同的表中查找到所需要的信息呢
从不同的表中选择
假设你有一个名为DEPOSITS 表其内容如下
输出
DEPOSIT# WHOPAID AMOUNT REMARKS
1 Rich Uncle 200 Take off Xmas list
2 Employer 1000 15 June Payday
3 Credit Union 500 Loan
分析
你需要对数据源作一下简单的改动
查找不重复的数据
如果你看过原来的CHECKS 表你会发现其中有一些数据是重复的例如AMOUNT
列
输入
SQL> select amount from checks
输出
AMOUNT
150
245.34
200.32
98
150
25
25.1
请注意150 在这里是重复的如果你只想查看不重复的数据可以这样做
输入
SQL> select DISTINCT amount from checks;
输出
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 39
AMOUNT
25
25.1
98
50
200.32
245.34
6 rows selected
分析
注意只有六行数据被选择这是因为你使用了DISTINCT 所有只有不重复的数据才
会被显示ALL 是在SELECT 中默认的关键字你几乎从来也不会用到ALL 因为SELECT
与SELECT ALL 是等价的
试一下这个例子作为你对SQL 的第一次也是唯一的一次实际体验
输入
SQL> SELECT ALL AMOUNT
FROM CHECKS;
输出
AMOUNT
150
245.34
200.32
98
150
25
25.1
7rowsselected.
它的结果与SELECT <Column>是相同的谁还会再去用这个多余关键字呢
总结
关键字SELECT 可以检索数据库并从中返回数据你可以用一个很长的语句并使用
SELECT *来检索数据库中的所有表而且你可以对指定表格的结果进行重新排序而关键
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 40
字DISTINCT 则会强制性地要求返回的结果中不能有重复数据明天我们将学习如何使您
的查询更具有选择性
问与答
问这些数据是从哪里来的我们是如何得到它的
答数据是按照第8 天所讲述的方法创建的与数据库的联接是依靠你所使用的SQL 它
以传统的命令行方式与数据库进行会话该数据库原来属于服务器或客户机范畴但
最近它已经被移植到了PC 机上
问可是我用不到这些数据库那我还可以用SQL 干什么
答你也可以在编程语言中使用SQL 一般的编程语言都支持内嵌的SQL 例如COBOL
你可以在它的环境中写SQL 并编译而Microsoft 公司则提供了应用程序接口函数以
允许编程人员在Visual Basic C 或C++中使用SQL Sybase and Oracle 提供的库也
允许你在编程时使用SQL Borland 公司则将SQL 置于Delphi 中本书中也将讨论SQL
在编程中的应用
校练场
在校练场里我们提出了一些问题以帮助你巩固自己所学这些练习可以提高你在学习
中的经验请试着回答和练习附录五问答与练习中的内容在开始明天的工作之前要
确保你已经知道了这些问题的答案
1 下列语句所返回的结果是否相同
SELECT * FROM CHECKS;
select * from checks
2 为什么下列查询不会工作
a. Select * b. Select * from checks
c. Select amount name payee FROM checks;
3
A select *
From checks
B select * from checks
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 41
C select * from checks
/
练习
1 使用今天早些时候的CHECKS 表的数据来写一个查询返回表中的number 和remark
列中的数据
2 将练习1 中的查询再写一遍以使得remark 列出现在第一位
3 使用CHECKS 表写一个查询来返回其中的不重复数据
斧子 - 2006-2-11 18:05:00
第三天表达式条件语句与运算
目标
在第二天我们学习了使用SELECT 语句和FROM 语句来对数据进行有趣味性也是非常有
用的运算在今天我们将对昨天学习的进行进一步的扩充我们将把新的方法应用到查
询表和行中引进新的子句和被称为运算的批量处理方法在第三天的阳光下你将学
会
知道什么叫作表达式以及如何来使用它们
知道什么叫作条件语句以及如何来使用它们
熟悉基本的子句WHERE 的使用
可以用算术比较字符和逻辑表达式来建立一个运算
学会将多种不同的运算结合在一起使用
注在今天的学习中我们来使用PERSONAL ORACLE7 来进行应用举例其它的SQL 环
境在命令运算以及结果显示上与它稍有不同但在遵循ANSI 标准的基础上它们的结果应
该是相同的
表达式
表达式的定义非常简单表达式可以返回一个值表达式的类型非常广泛它以包括各种
类型的数据如数字字符以逻辑型等其实在下列子句如SELECT 和FROM 中所占
成分中表达式最大在下边的例子中amount 就是一个表达式它可以返回amount 列中的
数据
SELECT amount FROM checks
而在下列语句中NAME ADDRESS PHONE ADDRESSBOOK是表达式
SELECT NAME, ADDRESS, PHONE
FROM ADDRESSBOOK
现在请检查一下下边的表达式
WHERE NAME = 'BROWN'
这里NAME = 'BROWN'是一个条件语句这是一个逻辑形表达式的实例NAME =
'BROWN'将根据=号来返回值TRUE 或FALSE
条件
如果你想在数据库中查找一个或一组特定的信息你需要使用一个或更多的条件条件可
以包含在WHERE 子句中在上一个例子中条件就是
NAME = 'BROWN'
如果你想知道在你们单位中上一个月有谁的工作时间超过了100 个小时你可能会写出下
边的条件语句
NUMBEROFHOURS > 100
条件语句可以让你建立一个选择查询在大多数情况下条件中包括变量常量和比较运
算在第一个例子中的变量是NAME 常量是'BROWN' 而比较运算符则为= 在第二个
例子中变量为NUMBEROFHOURS,常量为100 而比较运算符则是> 当您准备写一个条
件查询时你需要知道两个元素WHERE 子句和运算
WHERE 子句
Where 子句的语法如下
WHERE <SEARCH CONDITION>
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 43
Select From 和Where 在SQL 中最常使用的三个子句Where 只是当你的查询具有更大的
选择性没有Where 子句你可以用查询做得最多的有用工作是显示选定表中的所有记录
例如
输入
SQL> SELECT * FROM BIKES
这将会将BIKES 表中的所有数据按行列出
输出
NAME FRAMESIZE COMPOSITION MILESRIDDEN TYPE
TREK 2300 22.5 CARBONFIBER 3500 RACING
BURLEY 22 STEEL 2000 TANDEM
GIANT 19 STEEL 1500 COMMUTER
FUJI 20 STEEL 500 TOURING
SPECIALIZED 16 STEEL 100 MOUNTAIN
CANNONDALE 22.5 ALUMINUM 3000 RACING
假若你想要一台特定型号的自行车你应该键入
SQL> SELECT FROM BIKES WHERE NAME = BURLEY
你将只会收到一个记录
输出
NAME FRAMESIZE COMPOSITION MILESRIDDEN TYPE
BURLEY 22 STEEL 2000 TANDEM
分析
这个简单的例子显示出了你可以在数据库返回的数据中加以条件限制
运算
运算是你需要对从数据库中返回的数据进行数学处理时所用到的元素运算可以归为六组
数值型比较型字符型逻辑型和备注型以及混合型
数值型运算
数值型运算有加减乘除和取模前四个不用多说取模将返回一个除法结果中商的
余数部分这里有两个例子
5%2=1
6%2=0
对于有小数的数据不能应用取模运算如实数
如果你在进行数据运算时应用了几个运算符而没有在其中使用括号那么运算进行的次序
将是先乘后除再模后加减举例来说表达式2*6+9/3 其结果将是12+3=15 但是表
达式2* 6+9 /3 结果则为2*15/3=10 注意在这里你使用了括号有时表达式不会按你所
想像的那样得出期望的结果
加法(+)
你可以在许多场合下使用加号下面的语句将显示一个价格表
输入SQL> SELECT * FROM PRICE
输入如下右
现在请输入
SQL>SELECT ITEM WHOLESALE WHOLESALE 0.15 FROM PRICE OUTPUT
ITEM WHOLESALE
TOMATOES .34
POTATOES .51
BANANAS .67
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 44
ITEM WHOLESALE
TURNIPS .45
CHEESE .89
APPLES .23
这里对于下列产品的每一个价格数据加了15 分
ITEM WHOLESALE WHOLESALE+0.15
TOMATOES .34 .49
POTATOES .51 .66
BANANAS .67 .82
TURNIPS .45 .60
CHEESE .89 1.04
APPLES .23 .38
分析
请不要忽视最后一列WHOLESALE+0.15 它在原始的数据库表中没有切记你在SELECT
中使用了*号这将会显示出所有的列SQL 允许你创建一个虚拟列或对已有的列进组合
和修改后产生的派生列
请再输入一次刚才的语句
SQL> SELECT * FROM PRICE
右面是从表中返回的结果
ITEM WHOLESALE
TOMATOES .34
POTATOES .51
BANANAS .67
TURNIPS .45
CHEESE .89
APPLES .23
分析
输出的结果有时原始数据并没有被改变而标题为WHOLESALE+0.15 的列也不是表中的
固有列事实上由于这个列标题太不容易为人所注意所以你应该在它的上边再花一些
工夫
请输入
SQL> SELECT ITEM WHOLESALE WHOLESALE 0.15 RETAIL
FROM PRICE
其结果如右
ITEM WHOLESALE RETAIL
TOMATOES .34 .49
POTATOES .51 .66
BANANAS .67 .82
TURNIPS .45 .60
CHEESE .89 1.04
APPLES .23 .38
分析
斧子 - 2006-2-11 18:06:00
真棒你不但可以创建一个新列而且还可以对它安自己的需要进行重命名你可以按语
法列名别名来对任何一个列进行重命名注意在列名与别名之间有空格
例如输入
SQL> SELECT ITEM PRODUCE WHOLESALE WHOLESALE 0.25 RETAIL
FROM PRICE
重命名的列如下
PRODUCE WHOLESALE RETAIL
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 45
TOMATOES .34 .49
POTATOES .51 .66
BANANAS .67 .82
TURNIPS .45 .60
CHEESE .89 1.04
APPLES .23 .38
注一些SQL 解释器使用的语法为列名= 别名所以前一个例子要写成如下格式
SQL> SELECT ITEM PRODUCE
WHOLESALE
WHOLESALE 0.25 RETAIL
FROM PRICE
请检查你的SQL 解释器以确认它采用哪一种语法
你大概想知道当不在命令行状态时应如何使用别名吧很清楚你知道报表生成器是如何
工作的吗总有一天当有人让你写一个报表的生成器时你就会记住它而且不用却重复
Dr Codd 和IBM 已经做过的工作
到现在为止你已经看到了两种加号的用法第一种用法是在SELECT 子句中使用+号以
执行对数据的运算并将结果显示出来第二种用法是在WHERE 子句中使用加号在WHERE
中使用操作符可以在当你对数据有特定条件时具有更大的灵活性
在一些解释器中加号还同时肩负着进行字符运算的责任在稍后的几天中你将会看到这
一点
减法—
减号也有两种用途第一种用途是作为负号使用你可以使用HILOW 表来验证这项功能
SQL> SELECT * FROM HILOW
输出
STATE HIGHTEMP LOWTEMP
CA -50 120
FL 20 110
LA 15 99
ND -70 101
NE -60 100
例如这里对数据进行这样的运算
SQL> SELECT STATE HIGHTEMP LOWS LOWTEMP HIGHS FROM HILOW
STATE LOWS HIGHS
CA 50 -120
FL -20 -110
LA -15 -99
ND 70 -101
NE 60 -100
第二种用法很明显是作为减号从某一列中减去另一列例如
SQL> SELECT STATE
HIGHTEMP LOWS
LOWTEMP HIGHS
(LOWTEMP - HIGHTEMP) DIFFERENCE
FROM HILOW
STATE LOWS HIGHS DIFFERENCE
CA -50 120 170
FL 20 110 90
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 46
LA 15 99 84
ND -70 101 171
NE -60 100 160
注意这里使用了别名来对输入的错误进行更正这只不过是一种暂时的补救方法虽然这
不是永久的解决办法你是以后第21 天常见的SQL 错误及其解决方案会看到如何
对数据以及输入进行更正在那里你将学会如何对错误的数据进行更正
该查询不只是修正至少看起来是这样错误的数据而且还创建了一个新列以获得每个
记录的最高与最低的差价
如何你在一个字符型字段中意外地使用了减号你将会看到如下信息
SQL> SELECT STATE FROM HILOW
ERROR ORA-01722 invalid number
No rows selected
在不同的解释器中错误的号码可能会不同但是结果是相同的
除法
除法只有一种显而易见的应用在PRICE 表中它的应用如下
输入
SQL> SELECT * FROM PRICE
输出:
ITEM WHOLESALE
TOMATOES .34
POTATOES .51
BANANAS .67
TURNIPS .45
CHEESE .89
APPLES .23
rows selected.
在下边的例句中你可以成功地将销售价折半
输入/输出
SQL> SELECT ITEM WHOLESALE (WHOLESALE/2) SALEPRICE 2 FROM PRICE
ITEM WHOLESALE SALEPRICE
TOMATOES .34 .170
POTATOES .51 .255
BANANAS .67 .335
TURNIPS .45 .225
CHEESE .89 .445
APPLES .23 .115
6 rows selected.
在这个SELECT 语句中除法的作用是显而易见的只不过将商品半价销售有点太难以理解
了
乘法*
乘法的运算也非常直观再以Price 表为例
输入
SQL> SELECT * FROM PRICE
输出
ITEM WHOLESALE
TOMATOES .34
POTATOES .51
BANANAS .67
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 47
TURNIPS .45
CHEESE .89
APPLES .23
6 rows selected.
将表中的价格下调10%可以用如下方法来实现
输入/输出:
SQL>SELECT ITEM WHOLESALE WHOLESALE * 0.9 NEWPRICE
FROM PRICE
ITEM WHOLESALE NEWPRICE
TOMATOES .34 .306
POTATOES .51 .459
BANANAS .67 .603
TURNIPS .45 .405
CHEESE .89 .801
APPLES .23 .207
6 rows selected.
通过这些操作您可以在SELECT 语句中进行复杂的运算
取模%
取模运算将返回一个除法的余数部分以REMAINS 表举例如下
输入
SQL> SELECT * FROM REMAINS
输出
NUMERATOR DENOMINATOR
10 5
8 3
23 9
40 17
1024 16
85 34
6 rows selected.
斧子 - 2006-2-11 18:06:00
你也可以用NUMERATOR % DENOMINATOR 的结果来建立一个新列
输入/输出
SQL> SELECT NUMERATOR DENOMINATOR NUMERATOR%DENOMINATOR
REMAINDER FROM REMAINS
NUMERATOR DENOMINATOR REMAINDER
10 5 0
8 3 2
23 9 5
40 17 6
1024 16 0
85 34 17
6 rows selected.
在一些SQL 解释器中取模运算符为MOD 见第4 天函数— — 返回数据的再加工下边
的语句所得到的结果与上边的语句相同
SQL> SELECT NUMERATOR DENOMINATOR MOD NUMERATOR DENOMINATOR
REMAINDER FROM REMAINS
优先级别
在这一部分的例子中主要讲述在SELECT 语句中的优先级别数据库PRECEDENCE 的内
容如下
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 48
SQL> SELECT * FROM PRECEDENCE
N1 N2 N3 N4
1 2 3 4
13 24 35 46
9 3 23 5
63 2 45 3
7 2 1 4
用PRECEDENCE 来做如下例子
输入/输出
SQL> SELECT
2 N1+N2*N3/N4
3 (N1+N2)*N3/N4
4 N1+(N2*N3)/N4
5 FROM PRECEDENCE
N1+N2*N3/N4 (N1+N2)*N3/N4 N1+(N2*N3)/N4
2.5 2.25 2.5
31.26 28.15 31.26
22.8 55.2 22.8
93 975 93
7.5 2.25 7.5
看到了吗第一例与最后一例的结果是相同的如果你把第四列改写成为N1+N2*
(N3/N4) 那么其结果与上边的例子是相同的
比较运算
顾名思义比较运算就是将两个表达式进行比较并返回三个数值中的一个
True,False,Unknow,请等一下Unknow True 和False 的意义无需说明但是什么是Unknow
呢
为了便于理解什么是Unknow 你需要理解一下什么是NULL 在数据库领域内NULL
的意义就是在一个字段之中没有数据这与在该字段中数据为零或为空的不是同一个概念
为零或为空是一种特殊的数值而NULL 则表示在这个字段之中什么也没有如果你想进
行Field=9 的比较而Field 字段是空的那么比较的结果就会返回Unknow 由于Unknow
是一种不正常的状态所以大多数SQL 都会置其为无效并提供一种叫IS NULL 的操作来
测试Null 的存在
输入
SQL> SELECT * FROM PRICE
输出
ITEM WHOLESALE
TOMATOES .34
POTATOES .51
BANANAS .67
TURNIPS .45
CHEESE .89
APPLES .23
ORANGES
请注意WHOLESALE 字段在ORANGES 处没有输出这说明在这里的数值是空的由于
这里的WHOLESALE 字段的属性为数字所以空值在这里是显而易见的但是如果空值
是出现在ITEM 列中那么要将空值与空白值区分开来就是非常重要的了
请试着找一下空值
输入/输出:
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 49
SQL> SELECT * FROM PRICE WHERE WHOLESALE IS NULL
ITEM WHOLESALE
ORANGES
如你所见到的WHOLESALE 字段中的ORANGES 是唯一的一个空值因为它是不可见
的可是当你使用= 这个比较运算符时会有什么结果呢
输入/输出:
SQL> SELECT * FROM PRICE WHERE WHOLESALE = NULL
No rows selected
分析
你没有得到任何记录因为比较运算在这里返回的结果为FALSE 所以使用WHERE
SWHLESALE IS NULL 在这里比使用=更恰当它将会返回所有存在空值的记录
这个例子也是对使用= 进行的比较操作进行的完全展示这之中的WHERE 子句就不
用多说了下面简要说一下等号
在今天的早些时候你已经看到了在一些SQL 解释器中等号可以在SELECT 子句中用以给
搜索字段赋以别名而在WHERE 子句中它则用于比较操作并且它是从多个记录中捡选
所需要数值的一种有效手段试一下
输入
SQL> SELECT * FROM FRIENDS
输出
LASTNAME FIRSTNAME AREACODE PHONE ST ZIP(邮政编码)
BUNDY AL 100 555-1111 IL 22333
MEZA AL 200 555-2222 UK
MERRICK BUD 300 555-6666 CO 80212
MAST JD 381 555-6767 LA 23456
BULHER FERRIS 345 555-3223 IL 23332
现在让我们来找一下JD.MAST 的记录信息(在我们的这个表中这很容易但是你的朋友可
能不只这些也许像这样的记录你有成千上万)
输入/输出
SQL> SELECT * FROM FRIENDS WHERE FIRSTNAME = 'JD'
LASTNAME FIRSTNAME AREACODE PHONE ST ZIP
MAST JD 381 555-6767 LA 23456
结果如我们所愿再试一下
输入/输出
SQL> SELECT * FROM FRIENDS WHERE FIRSTNAME = 'AL'
LASTNAME FIRSTNAME AREACODE PHONE ST ZIP
BUNDY AL 100 555-1111 IL 22333
MEZA AL 200 555-2222 UK
注你应该看到在这里= 号返回了多个记录注意第二个记录的邮政编码ZIP 是空
的邮政编码是一个字符型字段你将在第8 天学习如何创建和组装一个表这个特殊的
空字段表明在字符型字段中空字段与空白字段是不同的
此外还有一个关于敏感性的问题试一下
输入/输出
SQL> SELECT * FROM FRIENDS WHERE FIRSTNAME = BUD
FIRSTNAME
BUD
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 50
1 row selected
再试一下
输入/输出
SQL> select * from friends where firstname = Bud
No rows selected
分析
尽管SQL 对大小写是不敏感的但是数据库中的数据对大小写却是敏感的大多数公
司在存储数据时采用大写以保证数据的一致性所以你应该永远采用大写或小写来存储数
据大小写的混合使用会对你精确地查找数据造成障碍
斧子 - 2006-2-11 18:07:00
大于与大于等于
大于操作的使用方法如下
输入:
SQL> SELECT * FROM FRIENDS WHERE AREACODE > 300
输出
LASTNAME FIRSTNAME AREACODE PHONE ST ZIP
MAST JD 381 555-6767 LA 23456
BULHER FERRIS 345 555-3223 IL 23332
分析
这个操作将显示所有比区号比300 大的记录但是不包括300 如果要包括300 应写
成如下方式
输入/输出
SQL> SELECT * FROM FRIENDS WHERE AREACODE>=300
LASTNAME FIRSTNAME AREACODE PHONE ST ZIP
MERRICK BUD 300 555-6666 CO 80212
MAST JD 381 555-6767 LA 23456
BULHER FERRIS 345 555-3223 IL 23332
当然你使用AREACODE>299 时会得到相同的结果
注在这个语句中300 没有使用引号对于数字型字段是不需要加引号的
小于与小于等于
如你所料它们的使用方法与大于和大于等于操作相同但结果相反
输入
SQL> SELECT * FROM FRIENDS WHERE STATE< LA
输出
LASTNAME FIRSTNAME AREACODE PHONE ST ZIP
BUNDY AL 100 555-1111 IL 22333
MERRICK BUD 300 555-6666 CO 80212
BULHER FERRIS 345 555-3223 IL 23332
注为什么STATE 会变成ST 呢这是因为这一列只有两个字符宽所以结果只会返回两
个字符如果列为COWS 那么它将会显示成CO 而AREACODE 和PHONE 所在列的
列宽大于它们自身的名字所以它们不会被截去
分析
请等一下你现在知道<在字符字段中的用法了吗当然知道了你可以在各种数据类型
中进行你想要的比较操作结果会因数据类型的不同而不同例如你在下例中使用小写
字符
输入/输出
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 51
SQL>SELECT * FROM FRIENDS WHERE STATE < la
LASTNAME FIRSTNAME AREACODE PHONE ST ZIP
BUNDY AL 100 555-1111 IL 22333
MEZA AL 200 555-2222 UK
MERRICK BUD 300 555-6666 CO 80212
MAST JD 381 555-6767 LA 23456
BULHER FERRIS 345 555-3223 IL 23332
分析
因为大写的字母代码比小写的小所以大写的字符总是排在小写字符的前面这里再说一
次为了保证安全请在执行前检查大小写情况
技巧想知道你所进行操作的结果那你先要检查一下你的电脑所采用的字符编码集PC
机解释器使用的是ASCLL 编码而其它平台则使用EBCDIC 编码
要想在结果中显示Louisiana 键入
输入/输出
SQL> SELECT * FROM FRIENDS WHERE STATE<= LA
LASTNAME FIRSTNAME AREACODE PHONE ST ZIP
BUNDY AL 100 555-1111 IL 22333
MERRICK BUD 300 555-6666 CO 80212
MAST JD 381 555-6767 LA 23456
BULHER FERRIS 345 555-3223 IL 23332
不等号<>或!=
如果你想要查找一些除了确定信息以外的其它信息那你可以使用不等号由于SQL 的解
释器不同它可能写做<> 或!= 如果你想找除了AL 以外的人你可以写出
输入:
SQL> SELECT * FROM FRIENDS WHERE FIRSTNAME <> AL
输出
LASTNAME FIRSTNAME AREACODE PHONE ST ZIP
MERRICK BUD 300 555-6666 CO 80212
MAST JD 381 555-6767 LA 23456
BULHER FERRIS 345 555-3223 IL 23332
想找一下不在California 住的人可以写成
输入/输出:
SQL> SELECT * FROM FRIENDS WHERE STATE != CA
LASTNAME FIRSTNAME AREACODE PHONE ST ZIP
BUNDY AL 100 555-1111 IL 22333
MEZA AL 200 555-2222 UK
MERRICK BUD 300 555-6666 CO 80212
MAST JD 381 555-6767 LA 23456
BULHER FERRIS 345 555-3223 IL 23332
注意=和<>符号都表示不等于
字符操作
无论数据的输出是否为有条件输出你都可以对其中的字符串进行操作本部分将会讲述
两个操作符LIKE 和|| 以及字符串连接的概念
LIKE
如果你想从数据库中选出一部分数据并把它们添到一个模板中并且不需要非常精确的匹
配你可以用= 来对每一种可能的情况进行操作但是这一过程烦琐而又耗时这时
你可以使用LIKE 如下例
输入
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 52
SQL>SELECT * FROM PARTS
输出
NAME LOCATION PARTNUMBER
APPENDIX MID-STOMACH 1
ADAMSAPPLE THROAT 2
HEART CHEST 3
SPINE BACK 4
ANVIL EAR 5
KIDNEY MID-BACK 6
你怎样找出其中有BACK 的记录呢粗看一下这里有两个记录可不幸的是它们有一点差
别
请试一下
输入/输出:
SQL>SELECT * FROM PARTS WHERE LOCATION LIKE %BACK%
NAME LOCATION PARTNUMBER
SPINE BACK 4
KIDNEY MID-BACK 6
你可能注意到了在这条语句的LIKE 后边使用了% 在LIKE 表达式中%是一种通配符
它表示可能在BACK中出现的其它信息如果你输入如下
输入
SQL>SELECT * FROM PARTS WHERE LOCATION LIKE BACK%
你将会检索到所有以BACK开头的LOCATION 记录
输出
NAME LOCATION PARTNUMBER
SPINE BACK 4
如果你输入
输入
SQL> SELECT * FROM PARTS WHERE NAME LIKE A%
你将会得到所有NAME 中以开头的记录
输出
NAME LOCATION PARTNUMBER
APPENDIX MID-STOMACH 1
ADAMS APPLE THROAT 2
ANVIL EAR 5
那么LIKE 语句是否对大小写敏感呢请看下边的例子
输入/输出:
SQL> SELECT * FROM PARTS WHERE NAME LIKE 'a%'
no rows selected
分析
回答是敏感的当涉及到数据是时候总是大小写敏感的
如果你想查找在某一确定的位置上有字符的数据时你应该如何去做呢你可以使用另一个
通配符— — 下划线
下划线_
输入
SQL> SELECT * FROM FRIENDS
输出
LASTNAME FIRSTNAME AREACODE PHONE ST ZIP
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 53
BUNDY AL 100 555-1111 IL 22333
MEZA AL 200 555-2222 UK
MERRICK UD 300 555-6666 CO 80212
MAST JD 381 555-6767 LA 23456
BULHER FERRIS 345 555-3223 IL 23332
PERKINS ALTON 911 555-3116 CA 95633
BOSS SIR 204 555-2345 CT 95633
如果你想查找所有以开头的州可以使用如下语句
输入/输出
SQL> SELECT * FROM FRIENDS WHERE STATE LIKE C_
LASTNAME FIRSTNAME AREACODE PHONE ST ZIP
MERRICK BUD 300 555-6666 CO 80212
PERKINS ALTON 911 555-3116 CA 95633
BOSS SIR 204 555-2345 CT 95633
斧子 - 2006-2-11 18:08:00
也可以在一个语句中使用多个下划线如
输入/输出
SQL> SELECT * FROM FRIENDS WHERE PHONE LIKE 555-6_6_
LASTNAME FIRSTNAME AREACODE PHONE ST ZIP
MERRICK BUD 300 555-6666 CO 80212
MAST JD 381 555-6767 LA 23456
这个语句也可以写成如下形式
输入/输出
SQL> SELECT * FROM FRIENDS WHERE PHONE LIKE 555-6%
LASTNAME FIRSTNAME AREACODE PHONE ST ZIP
MERRICK BUD 300 555-6666 CO 80212
MAST JD 381 555-6767 LA 23456
看它们的结果是一样的这两个通配符也可以联合起来使用下边的例子将找出所有的
第个字母为的记录
输入输出
SQL> SELECT * FROM FRIENDS WHERE FIRSTNAME LIKE _L%
LASTNAME FIRSTNAME AREACODE PHONE ST ZIP
BUNDY AL 100 555-1111 IL 22333
MEZA AL 200 555-2222 UK
PERKINS ALTON 911 555-3116 CA 95633
连接
可以将两个字符串连接起来例如
输入
SQL> SELECT FIRSTNAME || LASTNAME ENTIRENAME FROM FRIENDS
输出
ENTIRE NAME
AL BUNDY
AL MEZA
BUD MERRICK
JD MAST
FERRIS BULHER
ALTON PERKINS
SIR BOSS
7 rows selected.
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 54
分析
请注意这里使用的是而不是号如果你试图使用号来连接两个字符串的话对于我
们使用的SQL 解释程序(Personal Oracle7)将会产生如下错误信息
输入/输出
SQL> SELECT FIRSTNAME LASTNAME ENTIRENAME FROM FRIENDS
ERROR
ORA-01722 invalid number
它试图将两个数字做加法运算但是它没有在表达式中找到任何数字
注有一些解释器也采用加号来连接字符串请检查一下你的解释器
对于连接字符串这里有更多的实例
输入/输出
SQL> SELECT LASTNAME || || FIRSTNAME NAME FROM FRIENDS
NAME
BUNDY AL
MEZA AL
MERRICK BUD
MAST JD
BULHER FERRIS
PERKINS ALTON
BOSS SIR
7 rows selected.
分析
这条语句在姓与名之间插入了一个逗号
注请注意在姓与名之间的多余的空格这些空格是数据的一部分对于确定的数据类型
空格将右填充至达到字段的设定宽度请检查你的解释器有关数据类型内容将在第9 天
的表的创建与维护中讨论
至现在为止你已经学完了所有的比较操作符对于一些问题这种方法非常好可是如果你
是想找出所有的名字中的第一个字母为P 并且他的应有的休假时间已经超过了3 天的人
呢
逻辑运算
逻辑运算用于SQL 的WHERE 子句中将两个或更多条件组合在一起
休假的时间总是人们在工作时讨论的热门话题现在我们来为财务部门设计一个名为渡假
VACATION 的表内容如下
输入
SQL> SELECT * FROM VACATION
输出
LASTNAME EMPLOYEENUM YEARS LEAVETAKEN
ABLE 101 2 4
BAKER 104 5 23
BLEDSOE 107 8 45
BOLIVAR 233 4 80
BOLD 210 15 100
COSTALES 211 10 78
6 rows selected.
假设你的公司的雇员每年可以有12 天的休假时间现在使用你所知道的逻辑运算来实现以
下要求名字是以B 开头并且他的休假时间已经超过了50 天的员工
输入/输出
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 55
SQL> SELECT LASTNAME YEARS * 12 LEAVETAKEN REMAINING
FROM VACATION WHERE LASTNAME LIKE B% AND
YEARS *12 LEAVETAKEN 50
LASTNAME REMAINING
BLEDSOE 51
BOLD 80
分析
这个查询语句是你讫今为止学到的最为复杂的语句SELECT 子句中使用了算术运算符来
确定每一个员工还有多少天剩余的假期标准的算式为YEARS * 12 – LEAVETAKEN 而
更为清楚的表达方法为YEARS * 12) LEAVETAKEN
LIKE 中使用了通配符%来发现所有的以B 开头的员工而比较运算的则用来发现所有休
假时间超过50 天的员工
这里我们使用了逻辑运算符号AND 来使查找到的记录同时满足两个条件带下划线的
AND
AND 只有当两个表达式的值都为真的时候才会返回真如果任意一个表达式的值不是真
那么结果就会是假的例如找一下在你的公司中工作不超过5 年但是剩余的休假时间超
过20 天的员工
输入
SQL> SELECT LASTNAME FROM VACATION WHERE YEARS<=5 AND
LEAVETAKEN>20
输出
LASTNAME
BAKER
BOLIVAR
如果你想知道在你的公司中工作时间年以上人员工和休假时间不足已有假期的的
员工呢你可以写成下边这样
输入/输出
SQL> SELECT LASTNAME WORKAHOLICS
2 FROM VACATION
3 WHERE YEARS >= 5
4 AND
5 ((YEARS *12)-LEAVETAKEN)/(YEARS * 12) < 0.50
WORKAHOLICS
BAKER
BLEDSOE
斧子 - 2006-2-11 18:08:00
给这些人放假吧也让我们结束对AND 的学习
OR
你也可以使用OR 来对几个条件进行合并当其中的任一个条件为真时其结果就会为真
值为了展示它与AND 的不同下面我们用OR 来换掉上一个例子中的AND
输入:
SQL> SELECT LASTNAME WORKAHOLICS
2 FROM VACATION
3 WHERE YEARS >= 5
4 OR
5 ((YEARS *12)-LEAVETAKEN)/(YEARS * 12) >= 0.50
输出:
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 56
WORKAHOLICS
ABLE
BAKER
BLEDSOE
BOLD
COSTALES
分析
上例中的结果仍然在其中但是我们又多个几个记录这几个记录出现的原因是它们满足
我们所提出的条件中的一个OR 只要记录满足其中的一个条件就会把记录返回
NOT
顾名思义它对条件取反条件为假时结果为真条件为真时结果为假
下边的SELECT 子句将返回所有开头的名字不是B 的员工
输入
SQL> SELECT *
2 FROM VACATION
3 WHERE LASTNAME NOT LIKE 'B%'
输出:
LASTNAME EMPLOYEENUM YEARS LEAVETAKEN
ABLE 101 2 4
COSTALES 211 10 78
当NOT 应用于NULL 时可以使用操作符IS 让我们再来看一下PRICES 表中WHOLESALE
列ORANGES 记录中的空值
输入/输出:
SQL> SELECT * FROM PRICE
ITEM WHOLESALE
TOMATOES .34
POTATOES .51
BANANAS .67
TURNIPS .45
CHEESE .89
APPLES .23
ORANGES
7 rows selected.
想找出所有的非空项可以写出如下语句
输入/输出:
SQL>SELECT * FROM PRICE WHERE WHOLESALE IS NOT NULL
ITEM WHOLESALE
TOMATOES .34
POTATOES .51
BANANAS .67
TURNIPS .45
CHEESE .89
APPLES .23
6 rows selected.
集合运算SET
在第一天介绍SQL 中我们已经知道了SQL 是基于集合的理论的下面这一部分将讨论
集合运算
UNION 与UNION ALL
UNION 将返回两个查询的结果并去除其中的重复部分下边有两个值勤人员表
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 57
输入:
SQL> SELECT * FROM FOOTBALL
输出:
NAME
ABLE
BRAVO
CHARLIE
DECON
EXITOR
FUBAR
GOOBER
7 rows selected.
输入:
SQL> SELECT * FROM SOFTBALL
输出:
NAME
ABLE
BAKER
CHARLIE
DEAN
EXITOR
FALCONER
GOOBER
7 rows selected.
在这两个表中有哪些不重复的人员呢
输入/输出:
SQL> SELECT NAME FROM SOFTBALL
2 UNION
3 SELECT NAME FROM FOOTBALL
NAME
ABLE
BAKER
BRAVO
CHARLIE
DEAN
DECON
EXITOR
FALCONER
FUBAR
GOOBER
10 rows selected.
UNION 返回了两个表中的10 个记录它们是不重复的但是两个表中共有多少人呢包
括重复的人员
输入/输出:
SQL> SELECT NAME FROM SOFTBALL
2 UNION ALL
3 SELECT NAME FROM FOOTBALL
NAME
ABLE
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 58
BAKER
CHARLIE
DEAN
EXITOR
FALCONER
GOOBER
ABLE
BRAVO
CHARLIE
DECON
EXITOR
FUBAR
GOOBER
14 rows selected.
分析
可以看到UNION ALL 与UNION 一样对表进行了合并但是它不去掉重复的记录可是
如果我们想知道都有谁同时在两个表中呢UNION 无法做到这一点我们需要学习使用
INTERSECT
INTERSECT 相交
INTERSECT 返回两个表中共有的行看下例它将返回两个表中有存在的员工
输入:
SQL> SELECT * FROM FOOTBALL
2 INTERSECT
3 SELECT * FROM SOFTBALL
输出
NAME
ABLE
CHARLIE
EXITOR
GOOBER
斧子 - 2006-2-11 18:09:00
分析
这些记录是两个表中都存在的
MINUS 相减
MINUS 返回的记录是存在于第一个表中但不存在于第二个表中的记录例如
输入:
SQL> SELECT * FROM FOOTBALL MINUS SELECT * FROM SOFTBALL
输出
NAME
BRAVO
DECON
FUBAR
上例中显示了三个不在垒球队中的足球队员如果你把语句的次序颠倒那么你将得到在
垒球队中但不在足球队中的队员
输入:
SQL> SELECT * FROM SOFTBALL MINUS SELECT * FROM FOOTBALL
输出
NAME
BAKER
DEAN
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 59
FALCONER
从属运算IN and BETWEEN
这两个运算符对你已经做过的例子提供了更快捷的操作如果你想找一个你在Colorado,
California,和Louisiana 的朋友可以输入
输入:
SQL> SELECT * FROM FRIENDS WHERE STATE= 'CA' OR STATE ='CO' OR STATE =
'LA'
输出
LASTNAME FIRSTNAME AREACODE PHONE ST ZIP
MERRICK BUD 300 555-6666 CO 80212
MAST JD 381 555-6767 LA 23456
PERKINS ALTON 911 555-3116 CA 95633
也可以输入
输入/输出:
SQL> SELECT * FROM FRIENDS WHERE STATE IN('CA','CO','LA')
LASTNAME FIRSTNAME AREACODE PHONE ST ZIP
MERRICK BUD 300 555-6666 CO 80212
MAST JD 381 555-6767 LA 23456
PERKINS ALTON 911 555-3116 CA 95633
分析
第二个实例比第一个更易读和简捷我想你一会再用以前的方法来工作了吧在IN 中也
可以使用数字例如
输入/输出:
SQL> SELECT *
2 FROM FRIENDS
3 WHERE AREACODE IN(100,381,204)
LASTNAME FIRSTNAME AREACODE PHONE ST ZIP
BUNDY AL 100 555-1111 IL 22333
MAST JD 381 555-6767 LA 23456
BOSS SIR 204 555-2345 CT 95633
如果你想要查找符合某一范围的记录例如
输入/输出:
SQL> SELECT * FROM PRICE WHERE WHOLESALE 0.25 AND WHOLESALE
0.75
ITEM WHOLESALE
TOMATOES .34
POTATOES .51
BANANAS .67
TURNIPS .45
或使用BEWTEEN 你可以这样写
输入/输出:
SQL>SELECT * FROM PRICE WHERE WHOLESALE BETWEEN 0.25 AND 0.75
ITEM WHOLESALE
TOMATOES .34
POTATOES .51
BANANAS .67
TURNIPS .45
看第二个是不是比第一个更清楚和易读
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 60
注如果批发价为0.25 的商品在表中存在那么它们也将会被返回BETWEEN 操作将包
括边界值
摘要
在第三天我们学会了使用最基本的SELECT 子句和FROM 子句现在我们已经掌握了最为
常用的操作来使数据库返回的结果满足你的要求你学会了使用算术比较字符和逻辑
操作这为你进一步学习SQL 打下了良好的基础
问与答
问如果我不想使用命令行的SQL 那么学习这些东西对我有什么用
答不论你使用内嵌SQL 的COBOL 还是微软的ODBC 它们所使用的SQL 结构都是一
样的所以你现在学习的东西将会更有助于你以后的学习
问既然SQL 是一种标准那为什么又种是让我检查一下自己的解释器呢
答我们所使用的是ANSI1992 标准但大多数供应商对它进行了修改以使它更适用于自
己的数据库我们是以ANSI1992 标准为基础的但在具体使用时要注意它们的不同
校练场
应用下表的内容来回答下列问题
LASTNAME FIRSTNAME AREACODE PHONE ST ZIP
BUNDY AL 100 555-1111 IL 22333
MEZA AL 200 555-2222 UK
MERRICK BUD 300 555-6666 CO 80212
MAST JD 381 555-6767 LA 23456
BULHER FERRIS 345 555-3223 IL 23332
PERKINS ALTON 911 555-3116 CA 95633
BOSS SIR 204 555-2345 CT 95633
写一下查询返回数据库中所有名字以M 开头的每一个人
写一个查询返回数据库ST 为LA 且FIRSTNAME 以AL 开头的人
给你两个表PART1 和PART2 你如何才能找出两个表中的共有元素请写出查询
WHERE a >= 10 AND a <=30 的更便捷写法是什么请写出来
下面的查询将返回什么结果
SELECT FIRSTNAME FROM FRIENDS WHERE FIRSTNAME='AL' AND
LASTNAME=‘BULHER’
练习
用上边给出的表返回下面的结果
NAME ST
AL FROM IL
输入
SQL> SELECT (FIRSTNAME || 'FROM') NAME, STATE
2 FROM FRIENDS
3 WHERE STATE = 'IL' AND
5 LASTNAME = 'BUNDY'
输出
NAME ST
AL FROM IL
仍使用上表返回以下结果
NAME PHONE
MERRICK, BUD 300-555-6666
MAST, JD 381-555-6767
BULHER, FERRIS 345-555-3223
斧子 - 2006-2-11 18:09:00
第四天函数对数据的进一步处理
目标
在今天我们将学习函数在SQL 中的函数可以执行一些储如对某一些进行汇总或或将
一个字符串中的字符转换为大写的操作在今天结束之际您将学会以下内容
l 汇总函数
l 日期与时间函数
l 数学函数
l 字符函数
l 转换函数
l 其它函数
这些函数将大大加强你对这一周的早些时间所学习的SQL 的基本功能所获得的数据的
操作能力开始的五个汇总函数COUNT SUM AVG MAX MIN 是由ANSI 标准
所制定的大多数的SQL 解释器都对汇总函数进行了扩充其中有一些今天会提到在有
些解释器这汇总函数的名称与这里所提到的不一样
汇总函数
这是一组函数它们返回的数值是基于一列的因为你不会对单个的记录求它的平均
数这一部分的例子将使用TEAMSTATS 表
输入
SQL>SELECT FROM TEAMSTATS
输出
NAME POS AB HITS WALKS SINGLES DOUBLES TRIPLES HR SO
JONES 1B 145 45 34 31 8 1 5 10
DONKNOW 3B 175 65 23 50 10 1 4 15
WORLEY LF 157 49 15 35 8 3 3 16
DAVID OF 187 70 24 48 4 0 17 42
HAMHOCKER 3B 50 12 10 10 2 0 0 13
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 63
CASEY DH 1 0 0 0 0 0 0 1
COUNT
该函数将返回满足WHERE 条件子句中记录的个数例如你想知道都有哪一个球员的
击球数小于350 可以这样做
输入/输出
SQL> SELECT COUNT( ) FROM TEAMSTATS WHERE HITS/AB .35
COUNT( )
4
为了使这段代码更易读可以使用别名
输入/输出
SQL>SELECT COUNT( ) NUM_BELOW_350 FROM TEAMSTATS
WHERE HITS/AB .35
NUM_BELOW_350
4
如果我们用列名来替换掉括号中的星号时会结果与原来有什么不同呢试一下
SQL> SELECT COUNT(NAME) NUM_BELOW_350 FROM TEAMSTATS
WHERE HITS/AB .35
NUM_BELOW_350
4
结果是一样的因为你所选择的NAME 列与WHERE 子句并不相关如果你在使用count
时无WHERE 子句那么它将会返回表中的所有记录的个数
输入/输出
SQL> SELECT COUNT( ) FROM TEAMSTATS
COUNT( )
6
SUM
SUM 就如同它的本意一样它返回某一列的所有数值的和如果想知道队员总打点的
总和是多少试一下
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 64
输入
SQL>SELECT SUM(SINGLES) TOTAL_SINGLES FROM TEAMSTATS
输出
TOTAL_SINGLES
174
如果想得到多个列的和可按如下所做
输入/输出
SQL> SELECT SUM(SINGLES) TOTAL_SINGLES SUM(DOUBLES)
TOTAL_DOUBLES SUM(TRIPLES) TOTAL_TRIPLES SUM(HR) TOTAL_HR
FROM TEAMSTATS
TOTAL_SINGLES TOTAL_DOUBLES TOTAL_TRIPLES TOTAL_HR
174 32 5 29
类似地如果想找一下所有的点数在300 包括300 以上的的队员则语句如下
输入/输出
SQL>SELECT SUM(SINGLES) TOTAL_SINGLES SUM(DOUBLES) TOTAL_DOUBLES
SUM(TRIPLES) TOTAL_TRIPLES SUM(HR) TOTAL_HR FROM TEAMSTATS
WHERE HITS/AB >=.300
TOTAL_SINGLES TOTAL_DOUBLES TOTAL_TRIPLES TOTAL_HR
164 30 5 29
想估计一下一个球队的平均中球率
输入/输出
SQL>SELECT SUM(HITS)/SUM(AB) TEAM_AVERAGE FROM TEAMSTATS
TEAM_AVERAGE
.33706294
SUM 只能处理数字如果它的处理目标不是数字你将会收到如下信息
输入/输出
SQL>SELECT SUM(NAME) FROM TEAMSTATS;
ERROR
ORA-01722 invalid number
no rows selected
该错误信息当然的合理的因为NAME 字段是无法进行汇总的
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 65
AVG
AVG 可以返回某一列的平均值如果你想知道漏球的平均数请看下例
输入
SQL>SELECT AVG(SO) AVE_STRIKE_OUTS FROM TEAMSTATS
输出
AVE_STRIKE_OUTS
16.166667
下边的例子反映了SUM 与AVG 的不同之处
输入/输出
SQL>SELECT AVG(HITS/AB) TEAM_AVERAGE FROM TEAMSTATS
TEAM_AVERAGE
.26803448
分析
可是在上一个例子中的打中率是.3370629 这是怎么回事呢AVG 计算的是打中的次
数与总打击次数商的平均值然而在上一个例子中是对打中次数和打击次数分别求和后在
进行相除的举例来说A 队员打了100 杆中了50 次那么他的平均值是0.5 B 队员
打了1 杆没打中他的平均值是0.0 而0.0 与0.5 的平均值是0.25 如果你按打101 杆
中50 杆计算那么结果就会是正确的了下边的例子将会返回正确的击中率
输入/输出
SQL>SELECT AVG(HITS)/AVG(AB) TEAM_AVERAGE FROM TEAMSTATS
TEAM_AVERAGE
.33706294
与SUM 函数一样AVG 函数也只能对数字进行计算
斧子 - 2006-2-11 18:09:00
MAX
如果你想知道某一列中的最大值请使用MAX 例如你想知道谁的打点最高
输入
SQL>SELECT MAX(HITS) FROM TEAMSTATS
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 66
输出
MAX(HITS)
70
你能从这里知道是谁打的最多吗
输入/输出
SQL>SELECT NAME FROM TEAMSTATS WHERE HITS=MAX HITS
ERROR at line 3
ORA-00934 group function is not allowed here
很不幸你不能这一信息提示你汇总函数无法在WHERE 子句中使用但是请别灰
心在第7 天的子查询深入SELECT 语句将引入子查询的概念并会给出知道谁是打
点最多人解决方案
如果把它用的非数字场合会有什么情况出现呢
输入/输出
SQL>SELECT MAX(NAME) FROM TEAMSTATS
MAX(NAME)
WORLEY
这是一个新现象MAX 返回了最高的字符串最大的是z 所以说MAX 既可以处
理数值也可以处理字符
MIN
MIN 与MAX 类似它返回一列中的最小数值例如你想知道打杆的最小值是多少
输入
SQL>SELECT MIN AB FROM TEAMSTATS
输出
MIN(AB)
1
下列语句将返回名字在字母表中排在最前边的
输入/输出
SQL>SELECT MIN(NAME) FROM TEAMSTATS
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 67
MIN(NAME)
CASEY
你可以同时使用MAX 和MIN 函数以获得数值的界限例如
输入/输出
SQL>SELECT MIN AB MAX AB FROM TEAMSTATS
MIN(AB) MAX(AB)
1 187
对于统计函数来说这一信息有时非常有用
注我们在今天开始曾说过这五个函数是由ANSI 标准所定义的其余的函数也已
经成为了事实上的标准你可以在所有的SQL 解释器中找到它们这里我们使用的它们
在ORACLE7 中的名字在其它的解释器中它们的名称可能与这里提到的不同
VARIANCE
VARIANCE 方差不是标准中所定义的但它却是统计领域中的一个至关重要的数
值使用方法如下
输入
SQL>SELECT VARIANCE HITS FROM TEAMSTATS
输出
VARIANCE(HITS)
802.96667
如果我们在将它应用于字符串
输入/输出
SQL>SELECT VARIANCE NAME FROM TEAMSTATS
ERROR
ORA-01722 invalid number
No rows selected
可见VARIANCE 也是一个只应用于数值对象的函数
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 68
STDDEV
这是最后一个统计函数STDDEV 返回某一列数值的标准差它的应用示例如下
输入
SQL>SELECT STDDEV HITS FROM TEAMSTATS
输出
STDDEV(HITS)
28.336666
同样对字符型列应用该函数时会得到错误信息
输入/输出
SQL>SELECT STDDEV(NAME) FROM TEAMSTATS
ERROR
ORA-01722 invalid number
no rows selected
这些统计函数也可以在一个语句中同时使用
输入/输出
SQL>SELECT COUNT AB AVG(AB) MIN(AB) MAX(AB) STDDEV(AB)
VARIANCE(AB) SUM(AB) FROM TEAMSTATS
COUNT(
AB)
AVG(A
B)
MIN(A
B)
MAX(
AB)
STDDEV(
AB)
VARIANCE
(AB)
SUM(A
B)
6 119.167 1 187 75.589 5712.97 715
当你下次见到比赛结果时你应该知道了SQL 正在它的后台工作
日期/ 时间函数
我们的生活是由日期和时间来掌握的大多数的SQL 解释器都提供了对它进行支持的
函数在这一部分我们使用PROJECT 表来演求日期和时间函数的用法
输入
SQL> SELECT * FROM PROJECT
输出
TASK STARTDATE ENDDATE
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 69
KICKOFFMTG 01-APR-95 01-APR-95
TECHSURVEY 02-APR-95 01-MAY-95
USERMTGS 15-MAY-95 30-MAY-95
DESIGNWIDGET 01-JUN-95 30-JUN-95
CODEWIDGET 01-JUL-95 02-SEP-95
TESTING 03-SEP-95 17-JAN-96
注这里的数据类型使用日期型大多数SQL 解释器都有日期型但是在语法的细则
上有不同之处
ADD_MONTHS
该函数的功能是将给定的日期增加一个月举例来说由于一些特殊的原因上述的
计划需要推迟两个月那么可以用下面的方法来重新生成一个日程表
输入
SQL>SELECT TASK STARTDATE ENDDATE
ORIGINAL_END ADD_MONTHS(ENDDATE,2) FROM PROJECT
输出
TASK STARTDATE ORIGINAL ADD_MONTH
KICKOFFMTG 01-APR-95 01-APR-95 01-JUN-95
TECHSURVEY 02-APR-95 01-MAY-95 01-JUL-95
USERMTGS 15-MAY-95 30-MAY-95 30-JUL-95
DESIGNWIDGET 01-JUN-95 30-JUN-95 31-AUG-95
CODEWIDGET 01-JUL-95 02-SEP-95 02-NOV-95
TESTING 03-SEP-95 17-JAN-96 17-MAR-96
尽管这种延误不太可能发生但是实现日程的变动却是非常容易的ADD_MONTHS
也可能工作在SELECT 之外试着输入
输入
SQL>SELECT TASK TASKS_SHORTER_THAN_ONE_MONTH
FROM PROJECT WHERE ADD_MONTHS(STARTDATE 1) ENDDATE
结果如下所示
输出
TASKS_SHORTER_THAN_ONE_MONTH
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 70
KICKOFF MTG
TECH SURVEY
USER MTGS
DESIGN WIDGET
分析
你将会发现这一部分中的几乎所有的函数都可能工作在不只一个地方但是如果没
有TO_CHAR 和TO_DATE 函数的帮助ADD_MONTH就无法在字符或数字类型中工作
这将在今天的晚些时候讨论
斧子 - 2006-2-11 18:10:00
LAST_DAY
LAST_DAY 可以返回指定月份的最后一天例如如果你想知道在ENDDATE 列中的
给出日期中月份的最后一天是几号时你可以输入
输入
SQL>SELECT ENDDATE LAST_DAY ENDDATE FROM PROJECT
结果如下
输出
ENDDATE LAST_DAY(ENDDATE)
01-APR-95 30-APR-95
01-MAY-95 31-MAY-95
30-MAY-95 31-MAY-95
30-JUN-95 30-JUN-95
02-SEP-95 30-SEP-95
17-JAN-96 31-JAN-96
如果是在闰年的最后一天呢
输入/输出
SQL>SELECT LAST_DAY( 1-FEB-95 ) NON_LEAP LAST_DAY( 1-FEB-96 )
LEAP
FROM PROJECT;
NON_LEAP LEAP
28-FEB-95 29-FEB-96
28-FEB-95 29-FEB-96
28-FEB-95 29-FEB-96
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 71
28-FEB-95 29-FEB-96
28-FEB-95 29-FEB-96
28-FEB-95 29-FEB-96
分析
结果当然是正确的可是为什么它输出了这么多行呢这是因为你没有指定任何列或
给出一个条件SQL 引擎对数据库中的每一条记录都应用了这一语句如果你想去掉这些
重复的内容可以这样写
输入
SQL>SELECT DISTINCT LAST_DAY('1-FEB-95') NON_LEAP LAST_DAY('1-FEB-96')
LEAP FROM PROJECT
在这句话中我们使用了关键字DISTINCT 参见第二天的介绍查询— —SELECT 语
句的使用来得到唯一的结果
输出
NON_LEAP LEAP
28-FEB-95 29-FEB-96
虽然在我的电脑上该函数可以正确地识别出闰年来但是如果你要将它应用于金融领
域那么请在你的解释器上试一下看一看它是否支持闰年
MONTHS_BETWEEN
如果你想知道在给定的两个日期中有多少个月可以像这样来使用
MONTHS_BETWEEN
输入
SQL>select task startdate enddate months between(Startdate,enddate) duration from project
输出
TASK STARTDATE ENDDATE DURATION
KICKOFF MTG 01-APR-95 01-APR-95 0
TECH SURVEY 02-APR-95 01-MAY-95 -.9677419
USER MTGS 15-MAY-95 30-MAY-95 -.483871
DESIGN WIDGET 01-JUN-95 30-JUN-95 -.9354839
CODE WIDGET 01-JUL-95 02-SEP-95 -2.032258
TESTING 03-SEP-95 17-JAN-96 -4.451613
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 72
请等一下结果看起来不太对劲再试一下
输入/输出
SQL> SELECT TASK STARTDATE ENDDATE
MONTHS_BETWEEN ENDDATE,STARTDATE DURATION FROM
PROJECT
TASK STARTDATE ENDDATE DURATION
KICKOFF MTG 01-APR-95 01-APR-95 0
TECH SURVEY 02-APR-95 01-MAY-95 .96774194
USER MTGS 15-MAY-95 30-MAY-95 .48387097
DESIGN WIDGET 01-JUN-95 30-JUN-95 .93548387
CODE WIDGET 01-JUL-95 02-SEP-95 2.0322581
TESTING 03-SEP-95 17-JAN-96 4.4516129
分析
如你所见MONTHS_BETWEEN 对于你所给出的月份的次序是敏感的月份值为负
数可能并不是一件坏事例如你可以利用负值来判断某一日期是否在另一个日期之前
下例将会显示所有在1995 年5 月19 日以前开始的比赛
输入
SQL>SELECT * FROM PROJECT
WHERE MONTHS_BETWEEN 19 MAY 95 STARTDATE)
输出
TASK STARTDATE ENDDATE
KICKOFF MTG 01-APR-95 01-APR-95
TECH SURVEY 02-APR-95 01-MAY-95
USER MTGS 15-MAY-95 30-MAY-95
NEW_TIME
如果你想把时间调整到你所在的时区你可以使用NEW_TIME 下边给出了所有的时
区
简写时区简写时区
AST or ADT 大西洋标准时间HST or HDT 阿拉斯加_夏威夷时间
BST or BDT 英国夏令时MST or MDT 美国山区时间
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 73
CST or CDT 美国中央时区NST 新大陆标准时间
EST or EDT 美国东部时区PST or PDT 太平洋标准时间
GMT 格伦威治标准时间YST or YDT Yukon 标准时间
你可以这样来调节时间
输入
SQL>SELECT ENDDATE EDT NEW_TIME ENDDATE EDT PDT FROM
PROJECT
输出
EDT NEW_TIME(ENDDATE EDT PDT
01-APR-95 1200AM 31-MAR-95 0900PM
01-MAY-95 1200AM 30-APR-95 0900PM
30-MAY-95 1200AM 29-MAY-95 0900PM
30-JUN-95 1200AM 29-JUN-95 0900PM
02-SEP-95 1200AM 01-SEP-95 0900PM
17-JAN-96 1200AM 16-JAN-96 0900PM
就像变魔术一样所有的时间和日期都变成以新的时区标准了
NEXT_DAY
NEXT_DAY 将返回与指定日期在同一个星期或之后一个星期内的你所要求的星期天
数的确切日期如果你想知道你所指定的日期的星期五是几号可以这样做
输入
SQL>SELECT STARTDATE NEXT_DAY STARTDATE FRIDAY FROM
PROJECT
返回结果如下
输出
STARTDATE NEXT_DAY(STARTDATE, 'FRIDAY')
01-APR-95 07-APR-95
02-APR-95 07-APR-95
15-MAY-95 19-MAY-95
01-JUN-95 02-JUN-95
01-JUL-95 07-JUL-95
03-SEP-95 08-SEP-95
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 74
分析
输出的结果告诉了你距你所指定的日期最近的星期五的日期
SYSDATE
SYSDATE 将返回系统的日期和时间
输入
SQL> SELECT DISTINCT SYSDATE FROM PROJECT
输出
SYSDATE
18-JUN-95 1020PM
如果你想知道在今天你都已经启动了哪些项目的话你可以输入
输入/输出
SQL> SELECT * FROM PROJECT WHERE STARTDATE SYSDATE
TASK STARTDATE ENDDATE
CODE WIDGET 01-JUL-95 02-SEP-95
TESTING 03-SEP-95 17-JAN-96
现在你已经看到了项目在今天所启动的部分
斧子 - 2006-2-11 18:10:00
数学函数
大多数情况下你所检索到的数据在使用时需要用到数学函数大多数SQL 的解释器都
提供了与这里相类似的一些数学函数这里的例子使用的表名字叫NUMBERS 内容如下
输入
SQL>SELECT * FROM NUMBERS
输出
A B A B
3.1415 4 -57.667 42
-45 .707 15 55
5 9 -7.2 5.3
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 75
ABS
ABS 函数返回给定数字的绝对值例如
输入
SQL>SELECT ABS A ABSOLUTE_VALUE FROM NUMBERS
输出
ABSOLUTE_VALUE ABSOLUTE_VALUE
3.1415 57.667
45 15
5 7.2
CEIL 和FLOOR
CEIL 返回与给定参数相等或比给定参数在的最小整数FLOOR 则正好相反它返回
与给定参数相等或比给定参数小的最大整数例如
输入
SQL>SELECT B CEIL B CEILING FROM NUMBERS
输出
B CEILING B CEILING
4 4 42 42
.707 1 55 55
9 9 5.3 6
输入/输出
SQL>SELECT A FLOOR A FLOOR FROM NUMBERS
A FLOOR A FLOOR
3.1415 3 -57.667 -58
-45 -45 15 15
5 5 -7.2 -8
COS COSH SIN SINH TAN TANH
COS SIN TAN 函数可以返回给定参数的三角函数值默认的参数认定为弧度制
如果你没有认识到这一点那你会觉得下例所返回的值是错误
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 76
输入
SQL>SELECT A COS A FROM NUMBERS
输出
A COS(A) A COS(A)
3.1415 -1 -57.667 .437183
-45 .52532199 15 -.7596879
5 .28366219 -7.2 .60835131
分析
你可能认为COS 45 的返回值应该为0.707 左右而不应该是0.525 如果你想让它
按照弧度制来计算那么你需要将弧度制转换成角度制由于360 角度为2 个弧度所以
我们可以写成
输入/输出
SQL>SELECT A COS A*0.01745329251994 FROM NUMBERS
A COS(A*0.01745329251994)
3.1415 .99849724
-45 .70710678
5 .9961947
-57.667 .5348391
15 .96592583
-7.2 .9921147
分析
这里的将角度转换成弧度后的数值三角函数也可以像下面所写的那样工作
输入/输出
SQL>SELECT A COS A*0.017453 COSH A*0.017453 FROM NUMBERS
A COS(A*0.017453) COSH(A*0.017453)
3.1415 .99849729 1.0015035
-45 .70711609 1.3245977
5 .99619483 1.00381
-57.667 .53485335 1.5507072
15 .96592696 1.0344645
-7.2 .99211497 1.0079058
输入/输出
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 77
SQL> SELECT A SIN A*0.017453 SINH A*0.017453 FROM NUMBERS
A SIN(A*0.017453) SINH(A*0.017453)
3.1415 .05480113 .05485607
-45 -.7070975 -.8686535
5 .08715429 .0873758
-57.667 -.8449449 -1.185197
15 .25881481 .26479569
-7.2 -.1253311 -.1259926
输入/输出
SQL> SELECT A TAN A*0.017453 TANH A*0.017453 FROM NUMBERS
A TAN(A*0.017453) TANH(A*0.017453)
3.1415 .05488361 .05477372
-45 -.9999737 -.6557867
5 .08748719 .08704416
-57.667 -1.579769 -.7642948
15 .26794449 .25597369
-7.2 -.1263272 -.1250043
EXP
EXP 将会返回以给定的参数为指数以e 为底数的幂值其应用见下例
输入
SQL>SELECT A EXP A FROM NUMBERS
输出
A EXP(A) A EXP(A)
3.1415 23.138549 -57.667 9.027E-26
-45 2.863E-20 15 3269017.4
5 148.41316 -7.2 .00074659
LN and LOG
这是两个对数函数其中LN 返回给定参数的自然对数例如
输入
SQL>SELECT A LN(A) FROM NUMBERS
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 78
输出
ERROR
ORA-01428 argument -45 is out of range
这是因为我们忽视了参数的取值范围负数是没有对数的改写为
输入/输出
SQL>SELECT A LN ABS A FROM NUMBERS
A LN ABS A A LN ABS A
3.1415 1.1447004 -57.667 4.0546851
-45 3.8066625 15 2.7080502
5 1.6094379 -7.2 1.974081
分析
注意你可以将ABS 函数嵌入到LN 函数中使用第二个对数函数需要两个参数其
中第二个参数为底数下例将返回以10 为底的B 列的对数值
输入/输出
SQL> SELECT B LOG B 10 FROM NUMBERS
B LOG(B,10) B LOG(B,10)
4 1.660964 42 .61604832
.707 -6.640962 55 .57459287
9 1.0479516 5.3 1.3806894
斧子 - 2006-2-11 18:10:00
MOD
其实我们已经见过MOD 函数了在第三天的表达式条件及操作就有它我们
知道在ANSI 标准中规定取模运算的符号为%在一些解释器中被函数MOD 所取代下例的
查询就返回了A 与B 相除后的余数
输入
SQL>SELECT A B MOD A B FROM NUMBERS
输出
A B MOD(A,B)
3.1415 4 3.1415
-45 .707 -.459
5 9 5
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 79
-57.667 42 -15.667
15 55 15
-7.2 5.3 -1.9
POWER
该函数可以返回某一个数对另一个数的幂在使用幂函数时第一个参数为底数第
二个指数
输入
SQL>SELECT A B POWER A B FROM NUMBERS
输出
ERROR
ORA-01428 argument 45 is out of range
分析
粗看时你可能会认为它不允许第一个参数为负数但这个印象是错误的因为像-4 这
样的数是可以做为底数的可是如果第一个参数为负数的话那么第二个参数就必须是
整数负数是不能开方的对于这个问题可以使用CEIL 或FLOOR 函数
输入
SQL>SELECT A CEIL B POWER A CEIL B FROM NUMBERS
输出
A CEIL B POWER(A,CEIL(B))
3.1415 4 97.3976
-45 1 -45
5 9 1953125
-57.667 42 9.098E+73
15 55 4.842E+64
-7.2 6 139314.07
现在就可以有正确的结果了
SIGN
如果参数的值为负数那么SIGN 返回-1 如果参数的值为正数那么SIGN 返回1
如果参数为零那么SIGN 也返回零请看下例
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 80
输入
SQL>SELECT A SIGN A FROM NUMBERS
输出
A SIGN A A SIGN A
3.1415 1 -57.667 -1
-45 -1 15 1
5 1 -7.2 -1
你也可以在SELECT WHERE 子句中使用SIGN
输入
SQL>SELECT A FROM NUMBERS WHERE SIGN A =1
输出
A
3.1415
5
15
SQRT
该函数返回参数的平方根由于负数是不能开平方的所以我们不能将该函数应用于
负数
输入/输出
SQL>SELECT A SQRT A FROM NUMBERS
ERROR
ORA-01428 argument '-45' is out of range
但是你可以使用绝对值来解除这一限制
输入/输出
SQL>SELECT ABS A SQRT ABS A FROM NUMBERS
ABS(A) SQRT(ABS(A))
3.1415 1.7724277
45 6.7082039
5 2.236068
57.667 7.5938791
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 81
15 3.8729833
7.2 2.6832816
0 0
字符函数
许多SQL 解释器都提供了字符和字符串的处理功能本部分覆盖了大部分字符串处理
函数这一部分的例子使用CHARACTERS 表
输入/输出
SQL> SELECT * FROM CHARACTERS
LASTNAME FIRSTNAME M CODE
PURVIS KELLY A 32
TAYLOR CHUCK J 67
CHRISTINE LAURA C 65
ADAMS FESTER M 87
COSTALES ARMANDO A 77
KONG MAJOR G 52
CHR
该函数返回与所给数值参数等当的字符返回的字符取决于数据库所依赖的字符集
例如示例的数据库采用了ASCLL 字符集示例数据库的代码列的内容为数字
输入
SQL>SELECT CODE CHR CODE FROM CHARACTERS
输出
CODE CH CODE CH
32 87 W
67 C 77 M
65 A 52 4
在数值32 处显示为空白因为32 在ASCLL 码表中是空格
CONCAT
我们在第3 天时学到过一个与这个函数所执行的功能相当的操作符号表示将两个
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 82
字符串连接起来CONCAT 也是完成这个功能的使用方法如下
输入
SQL>SELECT CONCAT FIRSTNAME LASTNAME "FIRST AND LAST NAMES"
FROM CHARACTERS
输出
FIRST AND LAST NAMES
KELLY PURVIS
CHUCK TAYLOR
LAURA CHRISTINE
FESTER ADAMS
ARMANDO COSTALES
MAJOR KONG
分析
当用多个词来做为别名时需对它们使用引号请检查你的解释器看看它是否支持别
名
需要注意的是尽管在看起来输出似乎是两列但实际上它仍是一列这是因为你所连
接的Firstname 字段的宽度为15 函数取得了该列中的所有数据包括其中用以补足宽度
的空格
INITCAP
该函数将参数的第一个字母变为大写此外其它的字母则转换成小写
输入
SQL>SELECT FIRSTNAME BEFORE INITCAP FIRSTNAME AFTER
FROM CHARACTERS
输出
BEFORE AFTER
KELLY Kelly
CHUCK Chuck
LAURA Laura
FESTER Fester
ARMANDO Armando
MAJOR Major
斧子 - 2006-2-11 18:11:00
LOWER 和UPPER
如你所料LOWER 将参数转换为全部小写字母而UPPER 则把参数全部转换成大写字
母
下例是用LOWER 函数和一个叫UPDATE的函数来把数据库的内容转变为小写字母
输入
SQL>UPDATE CHARACTERS SET FIRSTNAME='kelly'
WHERE FIRSTNAME='KELLY'
输出
1 row updated.
输入
SQL>SELECT FIRSTNAME FROM CHARACTERS
输出
FIRSTNAME FIRSTNAME
kelly FESTER
CHUCK ARMANDO
LAURA MAJOR
然后请您再输入
SQL>SELECT FIRSTNAME UPPER FIRSTNAME LOWER FIRSTNAME
FROM CHARACTERS
输出
FIRSTNAME UPPER(FIRSTNAME LOWER FIRSTNAME
kelly KELLY kelly
CHUCK CHUCK chuck
LAURA LAURA laura
FESTER FESTER fester
ARMANDO ARMANDO armando
MAJOR MAJOR major
现在你明白这两个函数的作用了吧
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 84
LPAD 与RPAD
这两个函数最少需要两个参数最多需要三个参数每一个参数是需要处理的字符串
第二个参数是需要将字符串扩充的宽度第三个参数表示加宽部分用什么字符来做填补
第三个参数的默认值为空格但也可以是单个的字符或字符串下面的句子中向字段中加
入了五个字符该字段的定义宽度为5
输入
SQL>SELECT LASTNAME LPAD LASTNAME 20 * FROM CHARACTERS
输出
LASTNAME LPAD LASTNAME 20 * )
PURVIS *****PURVIS
TAYLOR *****TAYLOR
CHRISTINE *****CHRISTINE
ADAMS *****ADAMS
COSTALES *****COSTALES
KONG *****KONG
分析
为什么只添加了5 个占位字符呢不要忘记LASTNAME 列是15 个字符宽在可见字
符的右方填充着空格以保证字符的定义宽度请检查一下你所用的解释器现在再试一下
右扩充
输入
SQL> SELECT LASTNAME RPAD LASTNAME 20 * FROM CHARACTERS
输出
LASTNAME RPAD(LASTNAME,20,'*'
PURVIS PURVIS *****
TAYLOR TAYLOR *****
CHRISTINE CHRISTINE *****
ADAMS ADAMS *****
COSTALES COSTALES *****
KONG KONG *****
分析
通过这个操作我们可以清楚地看到空格也是该字段内容的一部分这一事实了下边的两个
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 85
函数正是用于这一情况的
LTRIM 与RTRIM
LTRIM 和RTRIM至少需要一个参数最多允许两个参数第一个参数与LPAD 和RPAD
类似是一个字符串第二个参数也是一个字符或字符串默认则是空格如果第二个参
数不是空格的话那么该函数将会像剪除空格那样剪除所指定的字符如下例
输入
SQL> SELECT LASTNAME RTRIM LASTNAME FROM CHARACTERS
输出
LASTNAME RTRIM(LASTNAME)
PURVIS PURVIS
TAYLOR TAYLOR
CHRISTINE CHRISTINE
ADAMS ADAMS
COSTALES COSTALES
KONG KONG
你可以用下边的语句来确认字符中的空格已经被剪除了
输入
SQL> SELECT LASTNAME RPAD RTRIM LASTNAME 20 * FROM
CHARACTERS
输出
LASTNAME RPAD(RTRIM(LASTNAME)
PURVIS PURVIS**************
TAYLOR TAYLOR**************
CHRISTINE CHRISTINE***********
ADAMS ADAMS***************
COSTALES COSTALES************
KONG KONG****************
输出证明的确已经进行了剪除工作现在请再试一个LTRIM
输入
SQL>SELECT LASTNAME LTRIM LASTNAME C FROM CHARACTERS
输出
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 86
LASTNAME LTRIM(LASTNAME,
PURVIS PURVIS
TAYLOR TAYLOR
CHRISTINE HRISTINE
ADAMS ADAMS
COSTALES OSTALES
KONG KONG
注意第三行和第五行的已经没有了
REPLACE
它的工作就如果它的名字所说的那样该函数需要三个参数第一个参数是需要搜索
的字符串第二个参数是搜索的内容第三个参数则是需要替换成的字符串如果第三个
参数省略或者是NULL 那么将只执行搜索操作而不会替换任何内容
输入
SQL> SELECT LASTNAME REPLACE LASTNAME ST REPLACEMENT FROM
CHARACTERS
输出
LASTNAME REPLACEMENT
PURVIS PURVIS
TAYLOR TAYLOR
CHRISTINE CHRIINE
ADAMS ADAMS
COSTALES COALES
KONG KONG
如果存在第三个参数如么在每一个目标字符串中搜索到的内容将会被由第三个参数所指
定的字符串替换例如
输入
SQL> SELECT LASTNAME REPLACE LASTNAME ST ** REPLACEMENT
FROM CHARACTERS
输出
LASTNAME REPLACEMENT
PURVIS PURVIS
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 87
TAYLOR TAYLOR
CHRISTINE CHRI**INE
ADAMS ADAMS
COSTALES CO**ALES
KONG KONG
如果没有第二个参数那么只有将源字符串返回而不会执行任何操作
输入
SQL> SELECT LASTNAME REPLACE LASTNAME NULL REPLACEMENT
FROM CHARACTERS
输出
LASTNAME REPLACEMENT
PURVIS PURVIS
TAYLOR TAYLOR
CHRISTINE CHRISTINE
ADAMS ADAMS
COSTALES COSTALES
KONG KONG
斧子 - 2006-2-11 18:11:00
SUBSTR
这个函数有三个参数允许你将目标字符串的一部份输出第一个参数为目标字符串
第二个字符串是将要输出的子串的起点第三个参数是将要输出的子串的长度
输入
SQL>SELECT FIRSTNAME SUBSTR FIRSTNAME 2 3 FROM CHARACTERS
输出
FIRSTNAME SUB
kelly ell
CHUCK HUC
LAURA AUR
FESTER EST
ARMANDO RMA
MAJOR AJO
如果第二个参数为负数那么将会从源串的尾部开始向前定位至负数的绝对值的位置例
如
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 88
输入
SQL> SELECT FIRSTNAME SUBSTR FIRSTNAME -13 2 FROM CHARACTERS
输出
FIRSTNAME SU
kelly ll
CHUCK UC
LAURA UR
FESTER ST
ARMANDO MA
MAJOR JO
分析
切记FIRSTNAME 字段的宽度为15 这也就是为什么参数为-13 时会从第三个开始的原因
因为从15 算起向前算第13 个字符正好是第3 个字符如果没有第三个参数将会输出字
符串余下的部分
输入
SQL> SELECT FIRSTNAME SUBSTR FIRSTNAME 3 FROM CHARACTERS
输出
FIRSTNAME SUBSTR(FIRSTN
kelly lly
CHUCK UCK
LAURA URA
FESTER STER
ARMANDO MANDO
MAJOR JOR
看是不是将字符串余下的部分返回了
现在再来看一个例子
输入
SQL> SELECT * FROM SSN_TABLE
输出
SSN__________
300541117
301457111
459789998
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 89
如果直接阅读上边的结果是比较困难的比较好的解决办法是使用下划线请先想一下下
边语句的输出情况
输入
SQL> SELECT SUBSTR SSN 1 3 ||'-'||SUBSTR SSN 4 2 ||'-'||SUBSTR SSN
6 4
SSN FROM SSN_TABLE
输出
SSN_________
300-54-1117
301-45-7111
459-78-9998
注这在当数字特别大例如1 343 178 128 需要用逗号分隔时以及区位号码或电
话号码需要下划线分隔时特别有效
这是SUBSTR 的另一个非常有用的功能倘若你需要打印一个报表而其中一些列的宽度超
过了50 个字符时你可以使用SUBSTR 来减小列宽以使它更接近数据的真实宽度请看
一个下面的这两个例子
输入
SQL> SELECT NAME JOB DEPARTMENT FROM JOB_TBL
输出
NAME______________________________________________________________
JOB_______________________________DEPARTMENT______________________
ALVIN SMITH
VICEPRESIDENT MARKETING
1 ROW SELECTED.
分析
注意这几列已经换行显示了这例得阅读变行非常困难现在试一下下边的SELECT
语句
输入
SQL> SELECT SUBSTR(NAME, 1,15) NAME, SUBSTR(JOB,1,15) JOB,
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 90
DEPARTMENT
2 FROM JOB_TBL;
输出
NAME________________JOB_______________DEPARTMENT_____
ALVIN SMITH VICEPRESIDENT MARKETING
是不是变得好多了
TRANSLATE
这一函数有三个参数目标字符串源字符串和目的字符串在目标字符串与源字符
串中均出现的字符将会被替换成对应的目的字符串的字符
输入
SQL> SELECT FIRSTNAME, TRANSLATE(FIRSTNAME
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ
'NNNNNNNNNNAAAAAAAAAAAAAAAAAAAAAAAAAA) FROM
CHARACTERS
输出
FIRSTNAME TRANSLATE(FIRST
kelly kelly
CHUCK AAAAA
LAURA AAAAA
FESTER AAAAAA
ARMANDO AAAAAAA
MAJOR AAAAA
6 rows selected.
注意这个函数对大小写是敏感的
INSTR
如果需要知道在一个字符串中满足特定的内容的位置可以使用INSTR 它的第一个参
数是目标字符串第二个参数是匹配的内容第三和第四个参数是数字用以指定开始搜
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 91
索的起点以及指出第几个满足条件的将会被返回下例将从字符串的第二个字符开始搜
索并返回第一个以O 开头的字符的位置
输入
SQL>SELECT LASTNAME INSTR LASTNAME O 2 1 FROM CHARACTERS
输出
LASTNAME INSTR(LASTNAME,'O',2,1)
PURVIS 0
TAYLOR 5
CHRISTINE 0
ADAMS 0
COSTALES 2
KONG 2
6 rows selected
分析
默认第三个与第四个参数的数值均为如果第三个数值为负数那么将会从后向前搜索
LENGTH
LENGTH将返回指定字符串的长度例如
输入
SQL>SELECT FIRSTNAME LENGTH RTRIM FIRSTNAME FROM CHARACTERS
输出
FIRSTNAME LENGTH(RTRIM(FIRSTNAME))
kelly 5
CHUCK 5
LAURA 5
FESTER 6
ARMANDO 7
MAJOR 5
注意这里使用了函数RTRIM 否则LENGTH将全部返回15
斧子 - 2006-2-11 18:12:00
转换函数
转换函数有三个可以使你方便地将数据从一种类型变换为另一种类型本节的示例
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 92
使用表CONVERSIONS
输入
SQL> SELECT * FROM CONVERSIONS
输出
NAME TESTNUM
40 95
13 23
74 68
NAME 列为字符串该列的宽度为15 TESTNUM 列为数字
TO_CHAR
该函数的最初功能是将一个数字转换为字符型不同的解释器可能会使用它来转换其它的
数据类型例如日期型转换为字符型或者是拥有更多的参数下例展示了该函数的基本功
能
输入
SQL> SELECT TESTNUM TO_CHAR TESTNUM FROM CONVERT
输出
TESTNUM TO_CHAR(TESTNUM)
95 95
23 23
68 68
稍安勿燥下例可以证明它确实已经将TESTNUM 转换为字符型了
输入
SQL>SELECT TESTNUM LENGTH TO_CHAR TESTNUM FROM CONVERT
输出
TESTNUM LENGTH(TO_CHAR(TESTNUM))
95 2
23 2
68 2
分析
如果对数字使用LENGTH函数将会返回错误注意TO_CHAR 与在先前进过的CHR 不同
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 93
CHR 返回字符集中给定数字位置的一个字符或符号
TO_NUMBER
该函数与TO_CHAR 函数相对应显而易见它是将一个字符串型数字转换为数值型例
如
输入
SQL> SELECT NAME TESTNUM TESTNUM*TO_NUMBER NAME FROM CONVERT
输出
NAME TESTNUM TESTNUM*TO_NUMBER(NAME)
40 95 3800
13 23 299
74 68 5032
分析
如果该函数没有将NAME 转换为数值的话将会返回一个错误信息
其它函数
这里有三个函数可能对你是有用处的
GREATEST 与LEAST
这两个函数将返回几个表达式中最大的和最小的例如
输入
SQL> SELECT GREATEST ALPHA BRAVO FOXTROT DELTA
FROM CONVERT
输出
GREATEST
FOXTROT
FOXTROT
FOXTROT
分析
注意GREATEST 将会返回在字母表中最靠后的字符开头的字符串虽然看起来似乎没
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 94
有必要使用FROM 子句可是如果FROM 子句没有的话你将会收到一个错误信息每
一个SELECT 语句都需要FROM 子句由于给定的表有三行所以结果返回了三个
输入
SQL> SELECT LEAST 34 567 3 45 1090 FROM CONVERT
输出
LEAST(34 567 3 45 1090)
3 3 3
就像你看到的那样这两个函数也可以对数字进行处理
USER
该函数返回当前使用数据库的用户的名字
输入
SQL> SELECT USER FROM CONVERT
输出
USER
PERKINS
PERKINS
PERKINS
只有我一个人在使用这个数据库看SELECT 又一次对表中的每一行都返回了结果
该函数与在今天早些时候提及的日期函数类似甚至尽管USER 不是表中确实存在的列
但SELECT 仍然检索了表中的每一行
总结
好长的一天啊我们一共学习了47 个函数你无需记住每一个函数只需要知道它们
的大致类型算术函数日期/时间函数字符函数转换函数其它函数而当你写一
个查询的时候你有一个明确的目标就够了
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 95
问与答
问为什么在ANSI 标准中定义的函数这么少而不同的解释器又都定义了这么多的函数
答ANSI 标准是一个非常宽松的标准而且如果对所有的解释器生产厂家来说过多的限
制会使其走向破产的道路另一方面如果A 公司对SQL 补充了一个用于统计的函
数包而销路特别好的话那么B 公司和C 公司一定也会跟着做的
问我认为你对SQL 的介绍太简单了我的工作时应该如何去使用这些函数呢
答这个问题与一个都三角的教师所提出的问题类似事实上我也不知道如何却求一个等
腰三角形的面积所以我的回答是根据你的职业而定你的工作中需要用到哪些
函数你就去使用它而其它的对你来说则是没有必要掌握的这一观点也适用于你的
查询
校练场
1 哪个函数是用来将给定字符串的第一个字母变成大写而把其它的字符变成小写的
2 哪此函数的功能就如同它的名字含义一样
3 下边的查询将如何工作
SQL> SELECT COUNT(LASTNAME) FROM CHARACTERS;
4 下边的查询是干什么的
SQL> SELECT SUM(LASTNAME) FROM CHARACTERS;
5 哪个函数可以将FIRSTNAME 列与LASTNAME 列合并到一起
6 在下边的查询中6 是什么意思
输入
SQL> SELECT COUNT(*) FROM TEAMSTATS;
输出
COUNT(*)
6 7 下列语句将输出什么
SQL> SELECT SUBSTR LASTNAME,1,5 FROM NAME_TBL;
SQL 21 日自学通(V1.0) 翻译人笨猪
EMAIL wyhsillypig@163.com 96
练习
1 用今天的TEAMSTARTS 表来写一个查询用来显示谁的中球率低于0.25 中球率
的计算方法为hits/ab
2 用今天的CHARACTERS 表来写一个查询要求返回下边的结果
INITIALS__________CODE
K.A.P. 32
1 row selected.
斧子 - 2006-2-11 18:12:00
第五天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 解释器是否支持它
斧子 - 2006-2-11 18:13:00
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#
斧子 - 2006-2-11 18:13:00
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.
斧子 - 2006-2-11 18:14:00
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 的记录过滤掉了我们并不试图告诉
你不要结合使用这两种分组你在以后可能会有这方面的需要但是请不要随便地结合使
用这两种子句在上例的表中只有为数不多的几行否则这本书的内容需要用车来拉了
而在你的实际工作中数据库可能有成千上万行结合使用后造成的变化就不会像现在这样
明显了
斧子 - 2006-2-11 18:15:00
总结
在今天我们学习了与扩展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 的联合类似这条语句的结果仍然没有太大
的用处在我们对这种语句深入之前我们先回想并讨论一下别名的问题
斧子 - 2006-2-11 18:15:00
正确地找到列
当你将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
有成千上万列如果你所使用的平台足够快那么多表联合可能对系统的性能没有影
响可是如果你工作在一个比较慢的平台上联合可能会导致死机
斧子 - 2006-2-11 18:16:00
不等值联合
既然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.
使你有了英雄的称号当然表中重复的记录内容将会被更正
斧子 - 2006-2-11 18:16:00
总结
今天你学习了对选择的表进行了所有的可能的联合所得的结果是满足你所提出的选
择的信息的内容
没有了— — 现在你已经学习了关于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
斧子 - 2006-2-11 18:17:00
第七天子查询内嵌的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 只返回单一结果
的部分— — 汇总函数
斧子 - 2006-2-11 18:17:00
在子查询中使用汇总函数
像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
© 2000 - 2026 Rising Corp. Ltd.