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


结果集的大小 
获得结果集所涉及的表的数量 
多少用户会同时修改这些数据 
数据总量 
Total Quantity of Data 
必须访问的数据总量,是要考虑的最重要因素。一个查询方案,用于只有14 行数据的 emp表 
和4行数据的dept表时表现非常出色,但它可能完全不适用于有1 500万行数据的 
financial_flows 表与有 500 万行数据的 products 表的join操作。注意,以许多公司的标准来 
看,1 500 万行的表并不算特别大。所以结论是,没有确定目标容量之前,很难断定查询执行 
的效率。 
定义结果集的查询条件 
CriteriaDefining the Result Set 
在编写 SQL 语句时,多数情况下会涉及 where 子句的条件,而在子查询或视图(普通视图 
或内嵌视图)中可能有多个 where 子句。然而,过滤条件的效率有高有低,这会受到其他因 
素的极大影响,例如物理实现(将在第5章中讨论)及要访问的数据量等因素。 
为了定义结果集,必须从几个方面来考虑,包括过滤、主要SQL语句,以及庞大的数据量对查 
询的影响等。这是个复杂的问题,须做深度探讨,详见本章“过滤”一节。 
结果集的大小 
Size of the Result Set 
查询所返回的数据量(或是SQL语句改动的数据量),是个重要且常被忽略的因素。一般而言, 
这取决于表的大小和过滤条件的细节,但不都是这样。典型的情况是,若干个独立使用时效率 
不高的条件,结合起来使用时会产生极高的效率;例如,以“是否获得理工科或文科学位”作为 
查询学生姓名的条件,结果集会非常大,但如果同时使用这两个条件(获得这两个学位),则产 
生的结果集就会大幅缩小。 
从技术的角度来看,查询结果集的大小并不重要,重要的是最终用户的感觉。用户的耐心,在 
很大的程度上和预期返回的记录条数有关:用户只检索一条记录,则他期望非常快,他不会关 
心整个数据库有多大。更极端的例子是,查询之后并未返回任何结果:好的开发者都会努力使 
…………………………………………………………Page 36……………………………………………………………
返回少量记录或不返回记录的查询尽量快,因为对用户而言,最令人沮丧的事莫过于等待了数 
分钟后,看到“无相符数据”的结果;若是按下回车键后马上察觉查询语句有误,而又无法终止 
查询,等待就更为恼人。最终用户情愿等待的,是预期返回大量数据时。如果把每个过滤条件 
定义的特定结果集看作中间结果,而最终结果是它们的交集(在条件中用and相连)或并集(在 
条件中用or相连),那么小型中间结果集的交集很可能为空。换言之,更精确的条件经常是零结 
果集产生的主要原因。无论何时,只要查询有可能返回零结果集时,都应该先检查那个最大可 
能导致空结果集的条件——尤其是在该检查执行非常快捷时。不用说,条件的顺序与条件所在 
上下文的关系十分密切,这在稍后“过滤”一节中讲述。 
总结:熟练的开发者应该努力使响应时间与返回的记录数成比例。 
表的数量 
Number of Tables 
查询中涉及的表的数量,自然会对性能有所影响。这不是因为 DBMS 引擎不能很好地执行连 
接操作——恰恰相反,现代的DBMS都能非常高效地连接很多表。 
Join 
连接(JJooiinn) 
认为连接效率不高的想法,来自另一个对关系数据库的成见。通常的说法是不该连接太多表, 
建议的上限是 5 个。事实上,连接 15 个表也一样可以极高效地执行。但在连接大量表时, 
会产生一些额外的问题。 
当需要连接多个表时(例如 15 个),按常理你就应该质疑设计的正确性。回忆一下第1章的 
内容—— 表的一条记录陈述了某个事实,而且可以将它比作数学的公理,通过连接表的操作, 
可衍生出其他事实。但要清楚一点,即哪些是显而易见的事实,可以称为公理;哪些是较不明 
显的事实,必须推衍得到。如果我们需要花大量时间来推衍事实,或许最初选择的公理就不合 
适。 
对于优化器来说,随着表数量的增加,复杂度将呈指数增长。再次提醒,统计优化器通常有 
出色的表现,但同时其耗时在查询总响应时间中的比例也很高,尤其是在查询第一次执行时。 
如果表比较多,让优化器分析所有可能的查询路径,是非常不切实际的。除非查询语句是为方 
便优化器刻意编写的,否则,查询越复杂,优化器越容易“押错宝(bet on the wrong horse)”。 
编写涉及许多表的复杂查询时,若可以用好几种截然不同的方式进行连接,最终选择失误的 
几率很高。如果我们连接表 A、B、C 和 D,优化器可能没有足够的信息判断出A 直接与 D 连 
接的效率会很高。想以 distinct 解决记录重复问题的开发者,也常会遗漏连接条件。 
复杂查询与复杂视图 
我们必须明白,表面上看到的参与查询的表的数量可能不真实,有些表实际上是视图,它们有 
时很复杂。和查询一样,视图的复杂程度也差异极大。视图可以屏蔽字段、记录、甚至是字段 
和记录的组合,只让少数有权限的用户可以访问。视图从特定视角反映数据,从表的现存关系 
中推衍出新的关系。此时,视图可以看作查询的简略表达方式,这是视图最常见的用途之一。 
随着查询复杂度的增加,似乎应该把查询拆成一系列独立视图,每个视图代表复杂查询的一部 
分。 
总结:表明简单的查询背后,可能隐藏着复杂的视图。 
…………………………………………………………Page 37……………………………………………………………
不要走极端,完全不使用视图也不合理,一般它们并无坏处。然而,将视图用在复杂查询中时, 
我们多半只对视图返回数据中的一小部分感兴趣——可能是几十个字段中的几个字段——这 
时,优化器会试图将简单视图重新并入一段更大的查询语句中。但是,一旦查询复杂到一定程 
度,此方法就太复杂了,以至于难以保证效率。 
在某些情况下,视图的编写方式,能有效地预防优化器把它并入上级语句中。我已提过 rownum, 
那是 Oracle 使用的虚拟字段,用来显示记录最初被查到时的顺序。如果在视图中使用rownum, 
复杂性会进一步增加。任何想把参照了 rownum 的视图并入上级查询中的尝试,都会改变后续 
rownum 的顺序,所以此时不允许优化器改写查询。于是,复杂查询中这种视图将独立执行。 
DBMS 优化器常把视图原样并入语句中,把它当成语句执行的一步来运行(注2),而且只使用 
视图执行结果中所需要的部分。 
视图中执行的操作(典型的例子是通过join获取ID号对应的描述信息),往往与其所属查询的上 
下文无关;或者,查询条件很特殊,会淘汰组成视图的一些表。例如,对若干个表进行union 
得到的视图,代表了多个子类型,而上级查询的过滤器只针对其中一个子类型,所以unio其实 
是不必要的。将“视图”与“视图中出现的表”进行join也有危险,这会强制多次扫描该表并多次访 
问相同记录,但其实只扫描一次就足够了。 
当视图返回的数据远多于上级查询所需时,放弃使用该视图(或改用一个较简单的视图),通常 
可使效率大为改善。首先,用SQL 查询取代主查询中用到的视图。对视图的组成部分有了整体 
的了解之后,要去除严格意义上不必要的部分就容易多了。改用较简单视图的效果也不错,从 
查询中去除了不必要部分,执行速度快多了。 
许多开发者不愿在复杂查询中,再引入复杂的视图,他们认为这会使情况更为复杂。推导与分 
解复杂的SQL表达式的确有点令人生畏,不过,和高中时常做的数学表达式推导也差不多。在 
我看来,这有助于形成良好的编程风格,值得花些时间去掌握。对于渴望提高编程技巧的开发 
者来说,研究上述技巧有利于对
小说推荐
返回首页返回目录