《SQL语言艺术(PDF格式)》第19章


count(distinct decode(amount_diff;0;chr(1);account))…1 
bad_acct_count 
from 
glreport 
groupby 
deptnum; 
ledger; 
accounting_period 
这个新的查询,执行速度是原先的四倍。这丝毫不令人意外,因为三次的完整扫描变成了一次。 
注意,查询中不再有where子句:amount_diff上的条件已被“迁移”到了select列表中decode()函数 
执行的逻辑,以及由groupby子句执行的聚合(aggregation)中。 
使用聚合代替过滤条件有点特殊,这正是我们要说明的“九种典型情况”中的另一种—— 以聚合 
函数为基础获得结果集。 
总结:内嵌查询可以简化查询,但若使用不慎,可能造成重复处理。 
小结果集,间接条件 
Small Result Set; Indirect Criteria 
与上一节类似,这一节也是要获取小结果集,只是查询条件不再针对源表,而是针对其他表。 
我们想要的数据来自一个表,但查询条件是针对其他表的,且不需要从这些表返回任何数据。 
典型的例子是在第4章讨论过的“哪些客户订购了特定商品”问题。如第4章所述,这类查询可用 
两种方法表达: 
使用连接,加上 distinct 去除结果中的重复记录,因为有的客户会多次订购相同商品 
使用关联或非关联子查询 
如果可以使用作用于源表的条件,请参考前一节“小结果集,直接条件”中的方法。但如果找不 
到这样的条件,就必须多加小心了。 
取用第4章中例子的简化版本,找出订购蝙蝠车的客户,典型实现如下: 
select distinct orders。custid 
from orders 
join orderdetail 
on (orderdetail。ordid =orders。ordid) 
join articles 
on (articles。artid=orderdetail。artid) 
where articles。artname ="BATMOBILE" 
依我看,明确使用子查询来检查客户订单是否包含某项商品,才是较好的方式,而且也比较容 
易理解。但应该采用“关联子查询”还是“非关联子查询”呢?由于我们没有其他条件,所以答案 
…………………………………………………………Page 57……………………………………………………………
应该很清楚:非关联子查询。否则,就必须扫描orders表,并针对每条记录执行子查询——当orders 
表规模小时通常不会查觉其中问题,但随着orders表越来越大,它的性能就逐渐让我们如坐针毡 
了。 
非关联子查询可以用如下的经典风格编写: 
select distinct orders。custid 
from orders 
where ordid in(select orderdetails。ordid 
from orderdetail 
join articles 
on (articles。artid=orderdetail。artid) 
where articles。artname ="BATMOBILE") 
或采用from子句中的子查询: 
select distinct orders。custid 
from orders; 
(select orderdetails。ordid 
from orderdetail 
join articles 
on (articles。artid=orderdetail。artid) 
where articles。artname ="BATMOBILE") assub_q 
where sub_q。ordid =orders。ordid 
我认为第一个查询较为易读,当然这取决于个人喜好。别忘了,在子查询结果上的 in() 条件暗 
含了distinct处理,会引起排序,而排序把我们带到了关系模型的边缘。 
总结:如果要使用子查询,在选择关联子查询、还是非关联子查询的问题上,应仔细考虑。 
多个宽泛条件的交集 
Small Intersection of BroadCriteria 
本节讨论对多个宽泛条件取交集获得较小结果集的情况。在分别使用各个条件时,会产生大型 
数据集,但最终各个大型数据集的交集却是小结果集。 
继续上一节的例子。如果“判断订购的商品是否存在”可选择性较差,就必须考虑其他条件(否 
则结果集就不是小结果集)。在这种情况下,使用正规连接、关联子查询,还是非关联子查询, 
要根据不同条件的过滤能力和已存在哪些索引而定。 
例如,由于不太畅销,我们不再检索订购蝙蝠车的人,而是查找上周六购买某种肥皂的客户。 
此时,我们的查询语句为: 
select distinct orders。custid 
from orders 
join orderdetail 
…………………………………………………………Page 58……………………………………………………………
on (orderdetail。ordid =orders。ordid) 
join articles 
on (articles。artid=orderdetail。artid) 
where articles。artname ="SOAP" 
and 
这个处理流程很合逻辑,该逻辑和商品具有高可选择性时相反:先取得商品,再取得包含商品 
的明细订单,最后处理订单。对目前讨论的肥皂订单的情况而言,我们应该先取得在较短期间 
内下的少量订单,再检查哪些订单涉及肥皂。从实践角度来看,我们将使用完全不同的索引: 
第一个例子需要orderdetail表的商品名称、商品ID这两个字段上的索引,以及orders表的主键 
orderid上的索引;而此肥皂订单的例子需要orders表日期字段的索引、orderdetail表的订单ID字 
段的索引,以及articles表的主键orderid上的索引。当然,我们首先假设索引对上述两例都是最 
佳方式。 
要知道哪些客户在上星期六买了肥皂,最明显而自然的选择是使用关联子查询: 
select distinct orders。custid 
from orders 
where 
andexists (select 1 
from orderdetail 
join articles 
on (articles。artid=orderdetail。artid) 
where articles。artname ="SOAP" 
andorderdetails。ordid =orders。ordid) 
在这个方法中,为了使关联子查询速度较快,需要orderdetail表的 ordid字段上有索引(就可以 
通过主键artid取得商品,无需其他索引)。 
第3章已提到,事务处理型数据库(transactional database)的索引是种奢侈,因为它处在经常更 
改的环境中,维护的成本很高。于是选择“次佳”解决方案:当表orderdetail 上的索引并不重要, 
而且也有充足理由不再另建索引时,我们考虑以下方式: 
select distinct orders。custid 
from orders; 
(select orderdetails。ordid 
from orderdetail; 
articles 
where articles。artid=orderdetail。artid 
andarticles。artname ="SOAP") assub_q 
where sub_q。ordid =orders。ordid 
and 
…………………………………………………………Page 59……………………………………………………………
这第二个方法对索引的要求有所不同:如果商品数量不超过数百万项,即使artname字段上没有 
索引,基于商品名称条件的查询性能也不错。表orderdetail的artid字段可能也不需索引:如果商 
品很畅销,出现在许多订单中,则表orderdetail和articles之间的连接通过哈希或合并连接(merge 
join)更高效,而artid字段上的索引会引起嵌套的循环。与第一种方法相比,第二种方法属于索 
引较少的解决方案。一方面,我们无法承受为表的每个字段建立索引;另一方面,应用中都有 
一些“次要的”查询,它们不太重要,对响应时间要求也不苛刻,索引较少的解决方案完全满足 
它们的要求。 
总结:为现存的查询增加搜索条件,可能彻底改变先前的构想:修改过的查询成了新查询。 
多个间接宽泛条件的交集 
Small Intersection; Indirect BroadCriteria 
为了构造查询条件,需要连接(join)源表之外的表,并在条件中使用该表的字段,就叫间接条 
件(indirect criterion)。正如上一节“多个宽泛条件的交集”的情况,通过两个或多个宽泛条件的 
交集处理获取小结果集,是项艰难的工作;若是涉及多次join操作,或者对中心表(centraltable) 
进行join操作,则会更加困难——这是典型的“星形schema(starschema)”(第10章详细讨论), 
实际的数据库系统中经常遇到。对于多个可选择性差的条?
小说推荐
返回首页返回目录