键为(ordid; artid),即ordid为索引的第一个属性,那么我们可以利用索引找到与订单相关的记
录,就和第3章中讲的一样。但如果主键是(artid; ordid)就太不幸了(注意,就关系理论而言,
无论哪个版本都是完全一样),此时的访问效率比(ordid; artid)作为索引时要差,甚至一些数
据库产品无法使用该索引(注3),唯一的希望就是在 ordid 上加独立索引了。
连接了表 orderdetail和orders之后,来看articles表,这不会有问题,因为表 orderdetail 主键
包括 artid字段。最后,检查 articles 中的值是否为Batmobile。查询就这样结束了吗?未必结
束,因为用了distinct ,通过层层筛选的客户名还必须要排序,以剔除重复项目。
分析至此,可以看出这个查询有多种编写方式。下面的语句采用了古老的join语法:
select distinct c。custname
from customersc;
orders o;
orderdetail od;
articles a
where c。city= "GOTHAM"
and c。custid = o。custid
and o。ordid = od。ordid
and od。artid = a。artid
and a。artname = "BATMOBILE"
and o。ordered 》= somefunc
本性难移,我偏爱这种较古老的方式。原因只有一个:从逻辑的角度来看,旧方法突显出数据
处理顺序无足轻重这一事实;无论以什么顺序查询表,返回结果都是一样的。customers 表非
常重要,因为最终所需数据都来自该表,在此例中,其他表只起辅助作用。注意,没有适用于
…………………………………………………………Page 41……………………………………………………………
所有问题的解决方案,表连接的方式会因情况不同而异,而决定连接方式取决于待处理数据的
特点。
特定的SQL查询解决特定的问题,而未必适用于另一些问题。这就像药,它能治好这个病人,
却能将另一个病人医死。
蝙蝠车买主的进一步讨论
下面看看查询蝙蝠车买家的其他方法。我认为,避免在最高层使用distinct应该是一条基本规则。
原因在于,即使我们遗漏了连接的某个条件,distinct也会使查询“看似正确”地执行——无可否
认,较旧的SQL语法在此方面问题较大,但ANSI/SQL92 在通过多个字段进行表的连接时也可
能出现问题。发现重复数据容易,但发现数据不准确很难,所以避免在最高层使用distinct应该
是一条基本规则。
发现结果不正确更难,这很容易证明。前面使用 distinct 返回客户名的两个查询,都可能返回
不正确结果。例如,如果恰巧有多位客户都叫“Wayne”,distinct不但会剔除由同个客户的多张
订单产生的重复项目,也会剔除由名字相同的不同客户产生的重复项目。事实上,应该同时返
回具唯一性的客户ID和客户名,以保证得到蝙蝠车买家的完整清单。在实际中,发现这个问题
可不容易。
要摆脱 distinct,可考虑以下思路:客户在 Gohtam市,而且满足存在性测试,即在最近六个
月订购过蝙蝠车。注意,多数(但非全部) SQL 方言支持以下语法:
select c。custname
from customers c
where c。city= "GOTHAM"
and exists (select null
from orders o;
orderdetail od;
articles a
where a。artname = "BATMOBILE"
and a。artid = od。artid
and od。ordid = o。ordid
and o。custid = c。custid
and o。ordered 》= somefunc )
上例的存在性测试,同一个名字可能出现多次,但每个客户只出现一次,不管他有多少订单。
有人认为我对 ANSI SQL 语法的挑剔有点苛刻(指“蝙蝠车买主”的例子),因为上面代码中
customers表的地位并没有降低。其实,关键区别在于,新查询中customers表是查询结果的唯
一来源(嵌套的子查询会负责找出客户子集),而先前的查询却用了join。
这个嵌套的子查询与外层的 select关系十分密切。如代码第 11 行所示(粗体部分),子查询
参照了外层查询的当前记录,因此,内层子查询就是所谓的关联子查询(correlated subquery)。
此类子查询有个弱点,它无法在确定当前客户之前执行。如果优化器不改写此查询,就必须先
找出每个客户,然后逐一检查是否满足存在性测试,当来自Gotham市的客户非常少时执行效率
倒是很高,否则情况会很糟(此时,优秀的优化器应尝试其他执行查询的方式)。
我们还可以这样编写查询:
…………………………………………………………Page 42……………………………………………………………
select custname
from customers
where city = "GOTHAM"
and custid in
(select o。custid
from orders o;
orderdetail od;
articles a
where a。artname = "BATMOBILE"
and a。artid = od。artid
and od。ordid = o。ordid
and o。ordered 》= somefunc)
在这个例子中,内层查询不再依赖外层查询,它已变成了非关联子查询(uncorrelated
subquery),只须执行一次。很显然,这段代码采用了原有的执行流程。在本节的前一个例子中,
必须先搜寻符合地点条件的客户(如均来自GOTHAM),接着依次检查各个订单。而现在,订
购了蝙蝠车的客户,可以通过内层查询获得。
不过,如果更仔细地分析一下,前后两个版本的代码还有些更微妙的差异。含关联子查询的代
码中,至关重要的是orders 表中的 custid字段要有索引,而这对另一段代码并不重要,因为这
时要用到的索引(如果有的话)是表customers的主键索引。
你或许注意到,新版的查询中执行了隐式的 distinct。的确,由于连接操作,子查询可能会返回
有关一个客户的多条记录。但重复项目不会有影响,因为 in 条件只检查该项目是否出现在子
查询返回的列表中,且in不在乎某值在列表中出现了一次还是一百次。但为了一致性,作为整
体,应该对子查询和主查询应用相同的规则,也就是在子查询中也加入存在性测试:
select custname
from customers
where city = "GOTHAM"
and custid in
(select o。custid
from orders o
where o。ordered 》= somefunc
and exists (select null
from orderdetail od;
articles a
where a。artname = "BATMOBILE"
and a。artid = od。artid
and od。ordid = o。ordid))
或者:
…………………………………………………………Page 43……………………………………………………………
select custname
from customers
where city = "GOTHAM"
and custid in
(select custid
from orders
where ordered 》= somefunc
and ordid in (select od。ordid
from orderdetail od;
articles a
where a。artname = "BATMOBILE"
and a。artid = od。artid)
尽管嵌套变得更深、也更难懂了,但子查询内应选择 exists 还是in 的选择规则相同:此选择
取决于日期与商品条件的有效性。除非过去六个月的生意非常清淡,否则商品名称应为最有效
的过滤条件,因此子查询中用in 比 exists 好,这是因为,先找出所有蝙蝠车的订单、再检查
销售是否发生在最近六个月,比反过来操作要快。如果表 orderdetail 的artid字段有索引,这
个方法会更快,否则,这个聪明巧妙的举措就会黯然失色。
注意
每当对大量记录做存在性检查时,选择in还是exists须斟酌。
利于多数 SQL 方言,非关联子查询可以被改写成from 子句中的内嵌视图。然而,一定要记住
的是,in 会隐式地剔除重复项目,当子查询改写为 from 子句中的内嵌视图
小说推荐
- C语言实例教程(PDF格式)
- -Page 1-前 言Visual C+是开发运行于Windows 95和Windows NT环境下的Win32应用程序的可视化编程工具中最重要的成员之一,它为软件开发人员提供了完整的编辑、编译和调试工具和建立于Win32 API(ApplicationProgramming Interface)基
- 其他
- 最新章:第143章
- SQL 21日自学通(V3.0)(PDF格式)
- -Page 1-SQL 21 日自学通(V1.0 翻译人 笨猪目录目录 1译者的话 14第一周概貌 16从这里开始 16
- 其他
- 最新章:第170章
- C语言游戏编程从入门到精通(PDF格式)
- -Page 1-Page 2-Page 3-Page 4-Page 5-Page 6-Page 7-Page 8-Page 9-Page 10-Page 11-Page 12-Page 13-Page 14
- 其他
- 最新章:第4章
- JMS简明教程(PDF格式)
- -Page 1-JMS1.1规范中文版卫建军2007‐11‐22-Page 2
- 其他
- 最新章:第28章
- oracle从入门到精通(PDF格式)
- -Page 1-Oracle 从入门到精通-Page 2-资源来自网络,仅供学习 Oracle 从入门到精通一、SQL 8
- 其他
- 最新章:第37章
- 深入浅出MFC第2版(PDF格式)
- -Page 1-Page 2-山高月小山高月小 水落石出水落石出山高月小山高月小 水落石出水落石出-Page 3-深入淺出MFC(第版 使用Visual C 5.0 MFC 4.2)Dissecting MFC(Second Edition Using Visual C 5.0 MFC 4.2)侯俊
- 其他
- 最新章:第309章
- 软件工程实践者的思想(PDF格式)
- -Page 1-大 道 至 简—软件工程实践者的思想周爱民(Aimingoo 著-Page 2-序2004 年 11 月初爱民(Aimingoo)第一次把他的书稿给我,我翻看了一下,第一反应讲的是感想。这不错,在技
- 其他
- 最新章:第26章
- VB2008从入门到精通(PDF格式英文版)
- -Page 1(R)The eXperT’s Voice inBeginningVB 2008From Novice to ProfessionalChristian Gross-Page 2-Page 3-Beginning VB 2008From Novice to Professional■C
- 其他
- 最新章:第214章
- 电子电路大全(PDF格式)
- -Page 1-电力生产人员技能培训电路基础部分电路基础部分电电路路基基础础部部分分-Page 2-一、电路的基本概念和基本定律-Page 3-考试点1o 1、掌握电阻、独立电压源、独立电流源、11受控源、电容、电感、耦合电感、理想变压器诸元件的定义、性质2o 2、掌握电流、电压参考方向的概念223
- 其他
- 最新章:第353章