多个宽泛条件的交集
Small Intersection of Broad Criteria
本节讨论对多个宽泛条件取交集获得较小结果集的情况。在分别使用各个条件时,会产生大型数据集,但最终各个大型数据集的交集却是小结果集。
继续上一节的例子。如果“判断订购的商品是否存在”可选择性较差,就必须考虑其他条件(否则结果集就不是小结果集)。在这种情况下,使用正规连接、关联子查询,还是非关联子查询,要根据不同条件的过滤能力和已存在哪些索引而定。
例如,由于不太畅销,我们不再检索订购蝙蝠车的人,而是查找上周六购买某种肥皂的客户。此时,我们的查询语句为:
select distinct orders.custid
from orders
join orderdetail
on (orderdetail.ordid = orders.ordid)
join articles
on (articles.artid = orderdetail.artid)
where articles.artname = 'SOAP'
and <selective criterion on the date in the orders table>
这个处理流程很合逻辑,该逻辑和商品具有高可选择性时相反:先取得商品,再取得包含商品的明细订单,最后处理订单。对目前讨论的肥皂订单的情况而言,我们应该先取得在较短期间内下的少量订单,再检查哪些订单涉及肥皂。从实践角度来看,我们将使用完全不同的索引:第一个例子需要orderdetail表的商品名称、商品ID这两个字段上的索引,以及orders表的主键orderid上的索引;而此肥皂订单的例子需要orders表日期字段的索引、orderdetail表的订单ID字段的索引,以及articles表的主键orderid上的索引。当然,我们首先假设索引对上述两例都是最佳方式。
要知道哪些客户在上星期六买了肥皂,最明显而自然的选择是使用关联子查询:
select distinct orders.custid
from orders
where <selective criterion on the date in the orders table>
and exists (select 1
from orderdetail
join articles
on (articles.artid = orderdetail.artid)
where articles.artname = 'SOAP'
and orderdetails.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
and articles.artname = 'SOAP') as sub_q
where sub_q.ordid = orders.ordid
and <selective criterion on the date in the orders table>
这第二个方法对索引的要求有所不同:如果商品数量不超过数百万项,即使artname字段上没有索引,基于商品名称条件的查询性能也不错。表orderdetail的artid字段可能也不需索引:如果商品很畅销,出现在许多订单中,则表orderdetail和articles之间的连接通过哈希或合并连接(merge join)更高效,而artid字段上的索引会引起嵌套的循环。与第一种方法相比,第二种方法属于索引较少的解决方案。一方面,我们无法承受为表的每个字段建立索引;另一方面,应用中都有一些“次要的”查询,它们不太重要,对响应时间要求也不苛刻,索引较少的解决方案完全满足它们的要求。
总结:为现存的查询增加搜索条件,可能彻底改变先前的构想:修改过的查询成了新查询。






