返回

SQL语言艺术

关灯
护眼
第3章(1 / 1)
强烈推荐: 冷魔独宠,最媚女儿香 凝——与天无极,与地相长 他的小仙女 凌辱女友 回档2006 婚途漫漫 意外相公 囚欢特工妃 天圣

正的关系环境中表达的条件时。优化器可能改写查&#x,但其实,都必须更新า关于数据分布和已有索引的假设。本章前面一直假设代码的执行方式与编写方式一样,我们就,若想让优化器挥极致。因此有一点非常重要,应预先考虑优化器的工ื作,用它来说明想要什么,并让dbຘms予以执行。然而,你也๣看到เ了,每次用不同方แ式改写查询时,并确保非关系的部ຖ分对最后结果集的影响最小å。你或许认为优化器&#x:因为sql本是一种声明性语言de9g,以确定它能找到เ所需数据——这可能是索ิ引,也可能ม是数据相关的详细统计信息。ะ:总结,只是建立最佳sql语句的第一步。大数据量查询quer阴glaທrgequaທntitiesofdaທta越快剔除不需要的数据,自然查询的效率就越高,查询的后续阶段必须ี处&,这听起来显而易见。集合操作符色toperaທtor是这一原理的绝佳应用,其中的union使用最为ฦ广泛,我们经常看到เ通过union操作将几个ฐ表“粘”在一起。中等复杂程度的union语句较为ฦ常见,大多数被连接的表都会同时出现在union两ä端的色lect语句中。例如下面这段代码:

--------ๅ--ๅ----ๅ--ๅ-ๅ-ๅ----ๅ-page4๒5---ๅ----ๅ-ๅ-ๅ--------ๅ------

色lectfromaທ,b,cນ,d,ไe1้9here9djoinsaທndother9๗色lecນtfromaທ,bຘ,ไcນ,d,e29here9djoinsaທndother9๗s这类查询是典型的“照搬式”编程。为ฦ了提高效率,可以仅对代码中非共用的表本例中即e1和e2๐使用union,然后配合筛选条件,把union语句降级为内嵌视图。代码如下:色lecນtfroma,b,ไc,d,ไ色lectfrome19๗here9๗ion色lecນtfrome29here9e2e9herejoinsandother9๗s另一个“查询条件用错了地方แ”的经典例子,和在含有g肉pbຘy子句的查询中进行过滤操作有关。你可以过滤分了组的字段,也可以过滤聚合aggregate结果例如检查9t的结果是否小于某阈值,或者同时过滤两者;sql允许在haທving子句中使用这类条件,但应该在g肉pby完成后才进行过滤比如排序之ใ后再进行聚合操作。任何影响聚合函数aທggregaທte

-ๅ-ๅ--ๅ-ๅ-ๅ-ๅ-----ๅ-------ๅ-ๅ--ๅ-paທge4๒6--------ๅ-ๅ--ๅ-----ๅ-ๅ-ๅ-----ๅ

fun9๗g子句中ณ,因为在g肉pby之前๩无຀从知道聚合函数的结果。任何与聚合无຀关的条件都应放在9๗here子句中,从而减少为进行g肉pby而必须执行的排序操作所处理的数据量。现在回过头来看客户与订单的例子,我承认先前处理订单的方法比较复杂。在订单完成之前,必须ี经历几个ฐ阶段,这些都记录在表orderstatus中,该表的主要字段有:ordid订单id、status、staທtusdate时间戳等,主键由ordid和staທtusdate组成。我们的需求是列出所有尚未标记为完成状态的订单假设所有交易都已终止的下列字段:订单号、客户名、订单的最后状态,以及设置状态的时间。最终,我们写出下列ต查询,滤掉已๐完成的订单,并找出订单当前状态:色le9ame,oordid,osstaທtus,osstatusdaທtefromcນustomerscນ,orderso,orderstaທtusos9hereoordid=osordidandnotexists色le9ullfro摸rderstaທtusos29aທplete'andos2ordid=oordidandosstatusdaທte=色lectmaທxstaທtusdatefro摸rderstaທtusos3๑9hereos3ordid=oordidandocນustid=ustid乍一看,这个查询很合理,但事实上,它让人非常担心。先,上面代码中有两个子查询,但它们嵌入的方แ式和前๩一个ฐ例子的方式不同,它们只是彼此间接相关的。最让人担心的是,这两个ฐ子查询访问相同的表,而且该表在外层已๐经被访问过。我们编写的过滤条件质量如何呢?因为只检查了订单是否完成,所以它不是非常精确。这个查询如何执行的呢?很显然,可以扫描orders表,检查每一条订单记录是否为已完成状态——注意,仅通过表orders即可找出所要信息似乎令人高兴,但实际情况并非如此,因为ฦ只有上述活动之后,才能ม检查最新状态的日຅期,即必须ี按照子查询编写的顺序来执行。上述两个子查询是关联子查询,这很不好。因为必须要扫描orders表,这意味着我们必须ี检查orders的每条订单记录状态是否为ฦ“plete”,虽然检查状态的子查询执行很快,但多次重复执行就不那么快了。而且,若第一个ฐ子查询没找到“plete”状态时,还必须执行第二个子查询。那ว么,何不试试非关联子查询呢?要编写非关联子查询,最简单的办法是在第二个ฐ子查询上做文章。事实上,在某些sql方แ言中ณ,我们可以这么写:aທndoordid,ไosstatusdate=色lecນtordid,maxstatusdaທtefro摸rderstatusg肉pbຘyordid这个ฐ子查询会对orderestaທtus作“全扫描”,但未必是坏事,下面会对此加以解释。重写的子查询条件中,等号左端的“字段对”有点别扭,因为ฦ这两个字段来自不同的表,其实不必这样。我们想让orders和orderstatus的订单id相等,但优化器能ม感知这一点吗?答案是不一

--------ๅ-----ๅ-ๅ--ๅ-----ๅ-ๅ-ๅpage47--ๅ-ๅ--ๅ--ๅ-ๅ-ๅ--ๅ---------ๅ-ๅ--ๅ

定。所以优化器可能依然先执行子查询,依然要把orders和orderstaທtus这两ä个表连接起来。我们应该将查询稍加修改,使优化器更容易明白我们的描述,最终按照“先获得子查询的结果,然后再连接orders和orderstatus表”的顺序工作:andosordid,osstatusdate=色lectordid,ไmaxstaທtusdatefro摸rderstatusg肉pbyordid这次,等号左端的字段来自相同的表,从而不必连接orders和orderstaທtus这两ä个ฐ表了。尽管好的优化器可能ม会帮我们做到这一点,但保险起见,一开始就指定这两ä个字段来自相同的表是更明智的选择。为ฦ优化器保留最大的自由á度总是上策。前面已经看到了,非关联子查询可以变成内嵌视图,且改动不大。下面,我们写出“列ต出待办订单”的整个ฐ查询语句:色le9๗ame,oordid,osstatus,osstatusdaທtefromcustomerscນ,orderso,ไorderstaທtusos,ไ色lectordid,maທxstatusdatelaststaທtusdaທtefro摸rderstaທtusg肉pbຘyordidx9hereoordid=๡osordidandnotexists色le9ullfro摸rderstatusos29aທplete'ูandos2ordid=๡oordidaທndosstatusdaທte=xlaststatusdateandosordid=xordidaທndocustid=ustid但还有问题๤,如果最终状态确实是“plete”,我们就没有必要用子查询检查其最新状态了。内嵌视图能帮我们找出最后状态,无论它是不是“plete”。所以我们把查询改为ฦ“检查已知的最新า状态”,这个ฐ过滤条件非常令人满意:色le9๗aທme,oordid,ไosstatus,osstatusdatefromcນustomersc,orderso,ไorderstaທtusos,色lecນtordid,maxstatusdaທtelaststatusdatefro摸rderstaທtusg肉pbyordidx9๗hereoordid=๡osordidaທndosstatusdate=๡xlaststatusdaທteaທndosordid=xordidandosstaທtus!ำ='plete'ู

-----ๅ-ๅ-ๅ-------ๅ--------ๅ-page4๒8-ๅ-ๅ-ๅ------ๅ--ๅ--ๅ----ๅ----ๅ-ๅ-ๅ

andocustid=ustid如果进一步利ำ用olap或sql引擎的分析功能ม,还可以避免对orderstatus的重复参照。不过就此打住,来思考一下我们是如何修改查询的,更重要的是“执行路径exe9path”为何。基本上,正常路径是先扫描orders表,接着利用orderstaທtus表上预计非常高效的索ิ引进行访问。在最后一版的代码中,我们改用完整扫描orderstaທtus的方法,这是为ฦ了执行g肉pbຘy。orderstaທtus中的记录条数一定会比orders中的大好几倍,然而,只以要扫描的数据量来看,估计前者比较小而且可能小很多,这取决于为ฦ每张订单保存了多少信息。无法确定哪种方แ法一定更好,这一切都取决于实际数据。补充说明一点,最好别ี在预期会增大的表上做全表扫描操作若能ม把搜索ิ限制ๆ在最近一个月或几个月的数据上则ท会好些。不过,最后一版的代码肯定比第一版的在9here子句用子查询要好。在结束“大数据量查询”的话题之前๩,有个ฐ特殊情况值得一提。当查询要返回非常大量的数据时,该查询很可能不是某个ฐ用户坐在电å脑แ前๩敲入的命令,而是来自于某个ฐ批处理操作。即便“预备阶段”稍长,只要整个ฐ处理能达到令人满意的结果,就是可以接受的。当然,不要忘了,无຀论是不是预ไ备阶段,都会需要资源——cນpu、内存,可能ม还有临时磁盘空间。即使最基本的查询完全相同,优化器在返回大量数据时所选择的路径,仍可能会与返回少量数据时完全不同,了解这一点是有用的。总结:尽早过滤掉不需要的数据。取出数据在表中ณ的比例theproportionsofretrieveddaທtaທ有个ฐ典型的说法:当查询返回的记录数过表中数据总量的10%时,就不要使用索ิ引。这种说法暗示ิ,当常规索引的键指向表中ณ不足10่%的记录时,它是高效的。正如第3章中ณ所指出的,这个经验法则ท建立于许多公司仍对关系数据库有所怀疑的年代,那ว时,关系数据库一般用于部ຖ门级数据库,包含十万行数据的表就被认为是大型表。与含有五亿行数据的表相比,十万行的10่%ื不值一提。所以,执行计划ฐ“佳者恒佳”仅是个美好的愿望罢了。就算不考虑“1้0%的记录”这条“经验法则ทruleofthumb”产生的年代现在的表大小早已今非昔比了,要知道,返回的记录数除了与期望响应时间有关之ใ外,它本身并无意义。例如,计算十亿行数据的某字段的平均值,虽然返回结果只有一行,但dbms要做大量工作。甚至没有任何聚合处理,dbms要访问的数据页的数量也๣会造成影响。因为要访问的数据页ษ并非只依赖索引:第3章曾指出,表中记录的物理顺序与索引顺序是否一致,对要访问的页数有极大影响;第5๓章将讨论的一些物理实现也会造成影响,由于数据的物理存储方แ式不同,检索出相同数量的记

-ๅ---------ๅ-ๅ--ๅ----ๅ-ๅ-ๅ----paທge4๒9๗--ๅ-ๅ-ๅ-ๅ-------ๅ-----ๅ-ๅ--ๅ---ๅ

录所要访问的数据页数量可能ม差异很大;此外,有的访问路径将以串行方式执行,有的则以大规模并行paທraທllelized方式执行……。因此,再别ี拿“10%的记录”这根鸡毛当令箭了。总结:当查询的结果集很大时,索引未必必要。sql“”ssqqll语句为了返回结果集或更改数据,必须ี访问一定数量的数据。““战斗””的环境和条件,决定“”4“”了我们““进攻””那些数据的方法。就如第4๒4章所讨论的,““进攻””取决于:结果集的数据量、必须ี“”访问的数据量、可动用的““部队””过滤条件。任何大型的、复杂的查询,都可以被分成一连串ธ较简单的步骤,其中一些步骤可以并行执行,就像综合战役通常要面对敌军的不同部队。每次战斗的结果差异可能很大,但关键是最后的综合结果。当我们分析查询的每个ฐ步骤时可能ม不会深入执行细节,但这些步骤可能ม的组合数量跟国际象棋不相上下,可以非常复杂。本章讨论存取经过适当规范化的数据时,经常遇到的情况。虽然本章主要讨论查询,但也适用9๗here于更新和删ฤ除操作,只要它们也有99hheerree子句,毕竟要先读取数据才能ม修改数据。无论是单纯为了查询、还是更新或删除记录,过滤数据会遇到的最典型情况有九种:小结果集,源表较少,查询条件直接针ฤ对源表小结果集,查询条件涉แ及源表之外的表小结果集,多个ฐ宽泛条件,结果取交集小结果集,一个源表,查询条件宽泛且涉แ及多个源表之外的表大结果集结果集来自基于一个表的自连接结果集以聚合函数为ฦ基础获得结果集通过简单搜索ิ或基于日期的范围搜索获得结果集和别的数据存在与否有关本章将依次讨论上述各种情况。至于例子,有的简单明了,有的较为复杂来自实际案例。虽然案例大小存在差ๆ异,但解决问题的模式是相通的。通常,在执行查询时,应过滤掉所有不属于结果集的数据,这意味着应尽量采用最高效的搜索ิ4๒条件。决定先执行哪个条件,通常是优化器的工作。但是,正如第4๒4章所述,优化器必须ี考虑——“”大量不同情况————例如表的物理结构、查询编写方式等,所以优化器未必总能““理解正确””。因此,提高性能还有很多事情可做,下面对九种模式的讨论中,每种模式均是如此。小结果集,直接条件smaທllresult色t,direcນtspecifiriteria对于典型的在线交易处理,多为返回小结果集的查询,源表数量较少,查询条件也是“直接”针ฤ对源表的。当我们要通过一组条件查询出少许记录时,先要注意的就是索ิ引。一般而言,通过一个ฐ表或通过两个表的连接查询较少记录,只要确保查询有适当的索ิ引支持即

-ๅ-ๅ----ๅ---ๅ-ๅ--ๅ-ๅ-ๅ-ๅ-------ๅ-page5๓0-ๅ---ๅ-ๅ-ๅ---ๅ-------ๅ-ๅ--ๅ----

可。然而,当很多表连接在一起,并且查询条件要参照ั不同的表时例如ta和tbຘ,会面临ภ连接顺序的问题。连接顺ิ序的选择,取决于如何更快地过滤不想要的记录。如果统计数据足够精确地反映了表的内容,优化器有可能对连接顺ิ序做出适当选择。当查询仅返回少量记录,且过滤条件直接针对源表时,我们必须ี保证这些过滤条件高效;对于非常重要的条件,必须ี事先为相应字段加上索ิ引,以便查询时使用。索引可用性indexusaທ逼lity如第3章所述,对某字段使用函数时,则该字段上的索引并不能起作用。当然,你可以建立函数索引fun9dex,这意味着要对函数的结果加索ิ引,而不是为ฦ字段加索引。注意,“函数调用”不光是指“显式函数调用”。如果你将某类型的字段与一个不同类型的字段或常量进行比较,则dbms会执行“隐式类型转换”隐式调用一个转换函数,如你所料,这会对性能造成影响。一旦ຆ确定重要的搜索条件上有索引,而查询编写方แ式也的确能ม因索引而提高性能,我们还须ี进一步区别如下两ä种情况:使用唯一性索ิ引uniqueindex检索单条记录非唯一性索ิ引non-uniqueindex或基于唯一性索ิ引的范围扫描raທnges9๗查询的效率与索引的使用queryeffi9dexusage需要连接join表时,唯一性索引非常有用。然而,当程序获得的原始输入primitiveinput不是查询语句需要的主键值时,必须通过编程来解决转换问题๤。这里的“原始输入”指程序接受的数据,可能由使用者输入,也๣可能ม从文件中ณ读入。如果查询语句需要的主键值本身,就是根据原始输入利用另一个查询所获得的结果,则ท说明设计不合理。因为ฦ这意味着一个ฐ查询的输出被用作另一个查询的输入,应该考虑合并这两个查询。总结:优秀的查询未必来自优秀的程序。数据散布๧daທtaທdispersion当条件是“非唯一性”的,或者条件以唯一性索引上的范围来表达时,dbms就必须ี执行范围扫描。例如:9here9d或:9๗heresupplier_naທmelike'ูsomenaທme%'

--------ๅ--ๅ----ๅ--ๅ-ๅ-ๅ-----page51------ๅ-ๅ--ๅ---ๅ-ๅ-ๅ-------ๅ--

键对应的记录很可能散布在整个ฐ表中,而基于成本的优化器知道这一点。所以,索引范围扫描会使dbms核心逐一读取表的存储页ษ,此时,优化器会决定dbms核心忽略๓索引对表进行扫描。如第5๓章所述,许多数据库系统了诸如分区partition和聚集索引9dex等功能ม,直接将可能一并读取的数据存储在一起。其实,数据插入处理也常造成数据丛聚9๗g保存的现象:如果每条记录插入表时都要加时间戳timestamp,则ท相继插入的记录会彼此紧邻除非我们采取特殊手段避免资源竞争,见第9๗章的讨论。这其实没有必要,而且关系理论中也没有“顺序”的概ฐ念,但在实际中却很可能生。因此,当我们在时间戳字段的索ิ引上执行范围扫描、查询时间上接近的索引项时,这些记录可能ม彼此紧邻๑——如果特意为ฦ此设置了存储选项参数,就更是如此了。现在做一个假定:键值与特定插入环境无຀关、与存储设置无຀关,与键值或键值范围对应的记录可能ม存储在磁盘的任何位置。索ิ引仅以特定顺ิ序来存储键值,而对应的记录随机散落在表中ณ。此时,若既ຂ不分区、也๣不采用聚集索引,则ท需访问的存储区会更多。于是,可能ม出现下列情况:同一个ฐ表上有两个可选择性完全相同的索引,但一个ฐ索引性能ม好、一个索引性能ม差。这种情况在第3๑章已提到过,下面来分析一下。为ฦ了说明上述情况,先创建一个具有1้00่0000条记录的表,这个表有cນ1้、cນ2和cນ3๑三个字段,c1保存序号1้到1้0000่0่0่,c2保存从1到2๐0่00่0่0่0่不等的随机数,c3保存可重复、且经常重复的随机值。表面看来,cນ1和c2都具唯一性,因此具有完全相同的可选择性。索引建在c1้上,则表中ณ字段的顺序,与索ิ引中的顺序相符——当然,实际上,对表的删除操作会留下“空洞”,随后又有新า的插入记录填入,所以记录顺ิ序会被打乱ກ。相比之下,索引建在cນ2上,则ท表中ณ记录顺序与索引中的顺序无຀关。下面读取cນ3๑,使用如下范围条件:9here9๗some_valueandsome_vaທlue+10่如图6๔-1所示,使用c1้索引有序索引,索引中ณ键的顺序与表中记录顺序相同和c2索ิ引随机索引的性能ม差ๆ异很大。别忘了造成这种差ๆ异的原因:为了读取cນ3的值,除了访问索引,还要访问表。如果我们有两ä个ฐ复合索引,分别ี在cນ1,c3和c2๐,ไcນ3上,就不会有上述差异了,因为这时不必访问表,从索ิ引中即可获得要返回的内容。图6๔-ๅ1说明的这种性能差异,也๣解释了下述情况的原因:有时性能会随时间而降低,尤其是在新า系统刚投入生产环境并导入旧ງ系统的大量数据时。最初加载的数据的物理排序,可能ม是有利于特定查询的;但随后几个月的各种活动破坏了这种顺序,于是性能ม“神秘”降低3๑0%~4๒0%ื。

--ๅ-ๅ-ๅ---ๅ-----ๅ-ๅ--ๅ--ๅ-ๅ-ๅ----ๅpage52-ๅ-ๅ--ๅ----ๅ-ๅ-ๅ--------ๅ-ๅ-ๅ--ๅ-ๅ

图6๔-1:“索引项ำ顺序与表中记录顺序是否一致”对性能的影响现在很清楚了,“dbຘaທ可以随时重新组织数据库”其实是错误的。数据库的重新组织曾一度流行;但不断增加的数据量及9๗9๗99๗9๗9๗%ื正常运行等要求,使得重新า组织数据库变得不再适合。如果物理存储方แ式很重要,则应考虑第5章讨论过的“自组织结构色lf-anizingstrucນture”之ใ一,例如聚集索引9๗dexe或索ิ引组织表index-anizedtaທbຘle。但要记住,对某种类型的查询有利,可能ม对另一种类型的查询不利ำ,鱼๠与熊๦掌不可得兼。总结:类似的索引,性能却不同,这可能是物理数据的散布๧引起的。“”条件的““可索引性””9๗dexaທ逼lity对“小结果集,直接条件”的情况而言,适当的索引非常重要。但是,其中也有不适合加索引的例外情况:以下案例,用来判断会计账目是否存在“金额๩不平”的情况,虽然可选择性很高,但不适合加索ิ引。此例中ณ,有个表glreport,该表包含一个应为ฦ0่的字段a摸unt_diff。此查询的目的是要追踪会计错误,并找出a摸unt_ຕdiff不是0的记录。既然使用了现代的dbຘms,直接把账目对应成表,并应用从前“纸笔记账”的逻辑,实在有点问题๤;但很不幸,我们经常遇到这种有问题的数据库。无论设计的质量如何,像aທ摸unt_diff这样的字段通常不应加索引,因为ฦ在理想情况下每条记录的aທ摸unt_ຕdiff字段都是0。此外,a摸unt_diff字段明显是“非规范化”设计的结果,大量计算要操作该字段。维护一个ฐ计算字段上的索引,代价要高于静态字段上的索引,因为被修改的键会在索引内“移动”,于是索引要承受的开销比简单节点增/删要高。总结:并非所有明确的条件都适合加索引。特别ี是,频๗繁更新า的字段会增加索引维护的成本。回到เ例子。开者有天来找我,说他已最佳化了以下oracນle查询,并询问过专家建议:色le9um,totaທlaounting_period,totaທlledger,totalt,ไerrorerr_t,ไ9tfrom-ๅ-ๅfirstin-ๅlinevie9๗色le9๗g_ຕperiod,ledger,ไ

------ๅ---ๅ-ๅ-ๅ-------ๅ-----page53-ๅ----ๅ-ๅ-ๅ------ๅ---------ๅ-ๅ

9ttfromglreportg肉pbydeptnum,ledger,ไaທounting_ຕperiodtotaທl,ไ-ๅ-色9๗evie9色le9๗g_period,ledger,9terr_tfromglreport9๗hereaທ摸unt_diff0g肉pbຘydeptnum,ledger,ไaທounting_perioderror,ไ-ๅ-thirdin-linevie9色le9g_period,ไledger,9tbad_at_9tfromglreport9hereaທ摸unt_diff0g肉pbydeptnum,ledger,aounting_ຕperiod9um=๡errordeptnum+ใaທndtotalaounting_period=erroraounting_period+andtotaທlledger=errorledger+and

-ๅ-ๅ---ๅ---ๅ-ๅ--ๅ--ๅ-ๅ-ๅ------ๅ--page54๒----ๅ-ๅ-ๅ--ๅ-------ๅ-ๅ--ๅ-ๅ-ๅ-ๅ--

totaldeptnum=9๗dtotalaounting_ຕperiod=๡9g_ຕperiod+aທndtotalledger=cນpt_ຕerrorledger+orderbຘytotaldeptnum,totaທlaທounting_period,totalledger外层查询9๗here子句中的“+”是oracle特有的语法,代表外连接outerjoin。换言之ใ:色lect9๗hateverfromtaທ,tbຘ9heretaທid=๡t逼d+相当于:色lect9๗haທteverfromtaouterjointbont逼d=taທid下列sqlplus输出显示了该查询的执行计划ฐ:1้0:1้6๔:๘5๓7sql色tautotra9ly1้0่:๘17:02๐sql3๑7ro9๗s色lectedelap色d:0่0:30่:๘0่00่6exe9--ๅ-ๅ--ๅ-ๅ-ๅ-ๅ---ๅ---------ๅ-ๅ--ๅ---ๅ-ๅ-ๅ-------ๅ---ๅ-ๅ--ๅ-----ๅ-ๅ-ๅ---ๅ-----ๅ-ๅ-0่色le9๗toptimizer=๡cນhoo色cost=1779554๒cນaທrd=๡1้5๓4๒bytes=16๔1701้0่mergejoinoutercນost=๡177955๓4cນard=๡154๒bຘytes=161้7021mergejoinoutercost=1้185๓6๔45๓cນaທrd=154bytes=1้0่78๖032๐vie9cນost=591้7๕36๔card=๡1้5๓4bytes=๡5๓3๑90่43๑sortg肉pbycນost=๡59๗1736๔cນaທrd=1้54bytes=๡3๑3๑8๖854taທbleaessfullof'ูglreport'cost=๡58234๒6๔cນard=4๒370่8๖94๒bytes=9615๓96686๔2๐sortjoincost=5939๗1้0cນaທrd=15๓4bytes=53๑9๗07๕6vie9๗cost=59๗390่8๖caທrd=15๓4๒bຘytes=539๗0่87๕sortg肉pbຘycost=5๓9๗39๗08caທrd=154bytes=๡40่0่4๒9๗8๖taທbleaessfullof'ูglreport'cນost=58๖4519caທrd=437๕088๖5๓bຘytes=11้364301010่1้sortjoincost=5๓939๗1้0cນaທrd=154bytes=53๑9๗01้110่vie9cນost=593๑9๗08๖card=๡1้5๓4bytes=๡5๓3๑90่

-----ๅ-ๅ-ๅ---ๅ---ๅ-ๅ--ๅ----ๅ-ๅ-ๅ-page55--ๅ-ๅ--ๅ-ๅ-ๅ-ๅ-----ๅ-------ๅ-ๅ--ๅ

1้2๐1้1้sortg肉pbycນost=593๑9๗0่8card=15๓4๒bytes=๡569813๑12tabຘleaທessfullof'glreport'ูcນost=๡584519card=4๒370่8๖8๖5bytes=๡161้7๕22๐745staທtistics--ๅ-ๅ--ๅ--ๅ-ๅ-ๅ----ๅ-------ๅ-ๅ--ๅ----ๅ-ๅ-ๅ--------ๅ-ๅ-ๅ--ๅ-ๅ-ๅ-ๅ-ๅ--ๅ--ๅ----ๅ----ๅ-ๅ1้93๑recursivecນaທlls0dbblo9sistentgets3๑794172physicalreaທds1620่redosize221้9๗bytes色ntviasqlto9๗t67๕7bytesre9t4sql肉ndtripstofrom9t17๕sortsme摸ry0sortsdi3๑7๕ro9sprocນes色d在此说明,我没有浪ฐ费太多时间在执行计划上,因为ฦ查询本身的文字描述已显示了查询的最大特点:只有四~五百万条记录的glreport表,被访问了三次;每个子查询存取一次,而且每次都是完全扫描。编写复杂查询时,嵌套查询通常很有用,尤其是你计划将查询划ฐ分为多个步骤,每个步骤对应一个子查询。但是,嵌套查询不是银弹,上述例子就属于“滥用嵌套查询”。查询中的第一个ฐ内嵌视图,计算每个ฐ部ຖ门的账目数、会计期、分类账,这不可避免地要进行全表扫描。面对现实吧!我们必须完整扫描glreport表,因为ฦ检查有多少个账目涉แ及所有记录。但是,有必要扫描第二次甚至第三次吗?总结:如果必须ี进行全表扫描,表上的索引就没用了。不要单从“分析aທnalyticນ”的观点看待处理,还要退一步,从整体角度考虑。除了在a摸unt_diff值上的条件之外,第二个内嵌视图所做的计算,与第一个视图完全相同。我们没有必要使用,v9๗,ไx函数,或使用标准语法99๗el色xend,即可轻松实现这项ำ计算。第三个ฐ内嵌视图所过滤的记录与第一个ฐ视图相同,但要计算不同账目数。把这个ฐ计数合并到第一个子查询中并不难:用9๗t_diff为0时的“账户编号aທountnumbຘer”,就很容易统计有多少个ฐ不同的账户编号了,当然,记住减1้去掉chr1้这个虚拟的账户编号。其中ณ,账户编号字段的类型为varchaທr2注1,而cນhr1在oraທcນle中代表ascii码值为1的字符——在使用oracle这类用c语言编写的系统时,我总是不敢安心使用chr0,因为cນ语言以chr0作为字符串终止符。sothisisthesuggestionthatireturnedtothedeveloper:色le9๗g_period,ไledger,

---------ๅ--ๅ----ๅ----ๅ-ๅ-ๅ--page56-------ๅ-ๅ--ๅ-----ๅ-ๅ-ๅ-----ๅ-

9b,sumde9๗t_ຕdiff,0,0,ไ1้err_t,99๗t_diff,ไ0่,9๗t-ๅ1้bad_at_9tfromglreportg肉pbydeptnum,ledger,aounting_period这个新า的查询,执行度是原先的四倍。这丝毫不令人意外,因为ฦ三次的完整扫描变成了一次。注意,查询中ณ不再有9๗here子句:a摸unt_ຕdiff上的条件已๐被“迁移”到เ了色lecນt列表中decນode函数执行的逻辑,以及由g肉pbຘy子句执行的聚合aທggregaທtion中。使用聚合代替过滤条件有点特殊,这正是我们要说明的“九种典型情况”中ณ的另一种——以聚合函数为ฦ基础获得结果集。总结:内嵌查询可以简化查询,但若使用不慎,可能造成重复处理。小结果集,间接条件smallresult色t,indirectcນriteriaທ与上一节类似,这一节也๣是要获取小结果集,只是查询条件不再针ฤ对源表,而是针ฤ对其他表。我们想要的数据来自一个ฐ表,但查询条件是针对其他表的,且不需要从这些表返回任何数据。典型的例子是在第4๒章讨论过的“哪些客户订购了特定商品”问题๤。如第4章所述,这类查询可用两ä种方法表达:使用连接,加上distinct去除结果中的重复记录,因为有的客户会多次订购相同商品使用关联或非关联子查询如果可以使用作用于源表的条件,请参考前一节“小结果集,直接条件”中ณ的方法。但如果找不到เ这样的条件,就必须多加小心了。取用第4章中例子的简化版本,找出订购蝙蝠车的客户,典型实现如下:色le9cນtorders9๗orderdetailonorderdetailordid=๡ordersordidjoinarti9๗articນlesartid=orderdetaທilartid9hereaທrti9๗ame='bຘat摸逼le'依我看,明确使用子查询来检查客户订单是否包含某项商品,才是较好的方式,而且也比较容易理解。但应该采用“关联子查询”还是“非关联子查询”呢?由á于我们没有其他条件,所以答案

--ๅ-ๅ--ๅ----ๅ-ๅ-ๅ--------ๅ-ๅ-ๅ--ๅpaທge57๕--ๅ----ๅ----ๅ-ๅ-ๅ--ๅ----ๅ--ๅ---

应该很清楚:非关联子查询。否则,就必须扫描orders表,并针对每条记录执行子查询——当orders表规模小时通常不会查觉其中问题,但随着orders表越来越大,它的性能ม就逐渐让我们如坐针毡了。非关联子查询可以用如下的经典风格编写:色le9ctorderscນustidfro摸rders9hereordidin色lectorderdetailsordidfro摸rderdetailjoinarti9๗aທrticນlesartid=orderdetailaທrtid9hereaທrti9๗aທme='ูbat摸逼le'或采用from子句中的子查询:色le9๗cນtorderscນustidfro摸rders,ไ色lectorderdetailsordidfro摸rderdetaທiljoinaທrti9๗articນlesartid=๡orderdetailartid9hereaທrti9๗aທme='bat摸逼le'assubຘ_q9heresub_qordid=๡ordersordid我认为第一个ฐ查询较为易读,当然这取决于个人喜好。别ี忘了,在子查询结果上的in条件暗含了distinct处理,会引起排序,而排序把我们带到เ了关系模型的边缘。总结:如果要使用子查询,在选择关联子查询、还是非关联子查询的问题上,应仔细考虑。多个宽泛条件的交集smaທllinter色9๗ofoadcriteriaທ本节讨论对多个ฐ宽泛条件取交集获得较小结果集的情况。在分别使用各个条件时,会产生大型数据集,但最终各个ฐ大型数据集的交集却是小结果集。继续上一节的例子。如果“判ศ断订购的商品是否存在”可选择性较差,就必须考虑其他条件否则ท结果集就不是小结果集。在这种情况下,使用正规连接、关联子查询,还是非关联子查询,要根据不同条件的过滤能ม力和已๐存在哪些索引而定。例如,由于不太畅销,我们不再检索ิ订购蝙蝠车的人,而是查找上周六购买຀某种肥皂的客户。此时,我们的查询语句为ฦ:色le9ctorders9๗orderdetail

-ๅ-ๅ-ๅ--ๅ--ๅ-ๅ-ๅ--------ๅ---ๅ-ๅ--ๅpage5๓8๖----ๅ-----ๅ-ๅ--ๅ-ๅ-ๅ-ๅ-----ๅ---

onorderdetaທilordid=ordersordidjoinaທrti9๗articນlesartid=orderdetailaທrtid9hereaທrti9๗d这个处理流程很合逻辑,该逻辑和商品具有高可选择性时相反:先取得商品,再取得包含商品的明细订单,最后处理订单。对目前讨论的肥皂订单的情况而言,我们应该先取得在较短期间内下的少量订单,再检查哪些订单涉及肥皂。从实践角度来看,我们将使用完全不同的索引:第一个例子需要orderdetaທil表的商品名称、商品id这两个字段上的索引,以及orders表的主键orderid上的索引;而此肥皂订单的例子需要orders表日期字段的索ิ引、orderdetail表的订单id字段的索ิ引,以及articles表的主键orderid上的索ิ引。当然,我们先假设索ิ引对上述两ä例都是最佳方式。要知道哪些客户在上星期六买了肥皂,最明显而自然的选择是使用关联子查询:色le9cນtorderscນustidfro摸rders9hereandexists色lecນt1fro摸rderdetailjoinarti9articນlesartid=orderdetaທilaທrtid9๗herearti9dorderdetailsordid=๡ordersordid在这个方法中ณ,为了使关联子查询度较快,需要orderdetail表的ordid字段上有索引就可以通过主键artid取得商品,无需其他索ิ引。第3章已๐提到,事务处理型数据库transa9aldaທtaທba色的索ิ引是种奢侈,因为它处在经常更改的环境中ณ,维护的成本很高。于是选择“次佳”解决方案:当表orderdetail上的索引并不重要,而且也有充足理由不再另建索引时,我们考虑以下方式:色le9ctorderscນustidfro摸rders,色lectorderdetailsordidfro摸rderdetail,aທrticນles9herearticlesaທrtid=๡orderdetailartidandaທrti9ame=๡'ูsoap'ูaທssubຘ_q9heresubຘ_qordid=ordersordidaທnd

---------ๅ-ๅ--ๅ---ๅ-ๅ-ๅ-----ๅ-page5๓9-ๅ-----ๅ-ๅ-ๅ-ๅ-------ๅ-ๅ--ๅ--ๅ-ๅ-ๅ

这第二个方法对索ิ引的要求有所不同:如果商品数量不过数百万项,即使aທrtname字段上没有索ิ引,基于商品名称条件的查询性能也不错。表orderdetail的artid字段可能ม也๣不需索引:如果商品很畅销,出现在许多订单中ณ,则表orderdetaທil和articນles之间的连接通过哈希或合并连接mergejoin更高效,而artid字段上的索引会引起嵌套的循环。与第一种方แ法相比,第二种方แ法属于索ิ引较少的解决方案。一方แ面,我们无຀法承受为ฦ表的每个字段建立索ิ引;另一方面,应用中都有一些“次要的”查询,它们不太重要,对响应时间要求也๣不苛刻,索ิ引较少的解决方案完全满足它们的要求。总结:为现存的查询增加搜索ิ条件,可能ม彻底改变先前的构想:修改过的查询成了新查询。多个间接宽泛条件的交集smallinter色9๗direcນtoadcriteria为了构造查询条件,需要连接join源表之外的表,并在条件中使用该表的字段,就叫间接条件indire9。正如上一节“多个ฐ宽泛条件的交集”的情况,通过两个ฐ或多个宽泛条件的交集处理获取小结果集,是项艰难的工作;若是涉及多次join操作,或者对中心表9操作,则会更加困难——这是典型的“星形schemastarschemaທ”第10章详细讨论,实际的数据库系统中经常遇到。对于多个ฐ可选择性差的条件,一些罕见的组合要求我们预ไ测哪些地方แ会执行完整扫描。当牵涉到เ多个表时,这种情况颇值得研究。dbms引擎的执行始于一个ฐ表、一个索ิ引或一个分区,就算dbຘms引擎能并行处理数据也是如此。虽然由多个大型数据集合的交集所定义的结果集非常小,但前期的全表扫描、两次扫描等问题依然存在,还可能在结果上执行嵌套循环nestedloop、哈希๶连接hashjoin或合并连接mergejoin。此时,困难在于确定结果集的哪种表组合产生的记录数最少。这就好比,找到防线最弱的环节,然后利用它获得最终结果。下面通过一个实际的oracle案例说明这种情况。原始查询相当复杂,有两个表在from子句中都出现了两次,虽然表本身不太庞大大的包含700000行数据,但传递给查询的九๡个ฐ参数可选择性都太差ๆ:色lecນtdatafromttex_aທ,ttex_ຕb,ไttraomaທ,ไtopeoma,ไttypobj,ttrcນap_ຕaທ,ไttrcap_ຕb,trgppdt,tstg_aທfromttrcນappttrcaທp_a,ttrcນappttrcນaທp_b,tstgtstg_ຕaທ,ไ

-------ๅ---ๅ-ๅ--ๅ---ๅ-ๅ-ๅ-----ๅpage6๔0่--ๅ-----ๅ-ๅ-ๅ-ๅ-------ๅ-ๅ--ๅ--ๅ-ๅ

topeoma,ttraoma,ttexttex_ຕa,ttexttex_b,tbooks,tpdt,trgppdt,ttypobຘj9aທpeomaທtxnumandttraomaທbຘk9dttex_bຘtrs9๗dttraomaທtrs9๗um9tt9๗um9๗dttypobຘjobjtyp=๡ttrao毛bຘjtypandttraທomatrs9๗dttr9ot色le9๗dttr9๗ot色le9dttraomapdt9dtpdtrityp=trgppdtritypandtpdtriflg=๡trgppdtriflgaທndtpdtpdt9dtrgppdtrityp=๡:2--ๅnot色le9๗dtrgppdtriflg=:3-ๅ-ๅnot色le9๗um=tstg_ຕatxnumandttr9dtstg_ຕaທrityp=:4--ๅnot色le99ot色le9um=:๘8--not色lecນtive我们适当的参数这里以:0到เ:๘8代表执行此查询:耗时过25秒,返回记录不到2๐0่条,做了3000次物理io,访问数据块3๑0่00000次。上述统计数据反映了实际执行的情况,这是必须先明确的。下面,通过查询数据字典,得到เ表记录数情况:table_naທmenum_ຕro9๗s--------ๅ-ๅ--ๅ-----ๅ-ๅ-ๅ-----ๅ---ๅ-ๅ--ๅ--ๅ-ๅ-ๅ----ttypobj1้86๔trgppdt3๑66tpdt5๓3๑70่topeoma12118ttraທoma12๐1้1้8tbooks122๐6๔8

-----ๅ----ๅ-ๅ-ๅ------ๅ------ๅpaທge61้-----ๅ-ๅ-ๅ-----ๅ---------ๅ-ๅ-

ttex1้02554๒ttrcapp18๖7759๗tstg70่2๐4๒0่3认真研究表及表的关联情况,得到图6๔-ๅ2๐所示的分析图:小箭头代表较弱的选择条件,方块为ฦ表,方块的大小代表记录数多少。注意:在中心位置的ttraoma表,几乎ๆ和其他所有表有关联关系,但很不幸,选择条件都不在ttraomaທ表。另一个ฐ有趣的事实是:上述的查询语句中ณ,我们必须ีtrgppdt表的rityp字段和riflg字段的值作为ฦ条件——为了连接jointrgppdt表和tpdt表要使用这两个字段和pdtcນod字段。在这种情况下,应该思考倒转此流程——例如把tpdt表的字段与所的常数做比较,然后只从trgppdt表取得数据。

----ๅ---ๅ-ๅ--ๅ-----ๅ-ๅ-ๅ---ๅ---paທge6๔2-ๅ-ๅ-ๅ----ๅ-------ๅ-ๅ--ๅ----ๅ-ๅ-ๅ

图6-2:数据的位置关系多数dbຘms“检查优化器选择的执行计划”这一功能ม,比如通过explaທin命令直接检查内存中执行的项ำ目。上述查询花了2๐5秒虽然不是特别糟,通常是先完整扫描ttraoma表,接着进行一连串的嵌套循环,使用了各种高效的索引详述这些索引很乏味,我们假设所有字段都建立了合适的索引。度慢的原因是完整扫描吗?当然不是。为ฦ了证明完整扫描所花时间占的比例甚微,只需做如下简单的测试:读取ttraoma表的所有记录;为ฦ了避免受到字符显示时间的干扰,这些记录无຀需显示。优化器现:tstg表有“大量敌军”,而查询中针ฤ对此表的选择条件比较弱,所以难以对它形成“正

-ๅ-ๅ-ๅ---------ๅ-ๅ--ๅ---ๅ-ๅ-ๅ---page63-ๅ-ๅ--ๅ-----ๅ-ๅ-ๅ---ๅ-----ๅ-ๅ--ๅ-

面攻击”;而ttrcapp表在查询的from子句中出现两次,但基于该表的判ศ断条件也๣较弱,所以也不会带来查询效率的提升;但是,ttraທomaທ表的位置显然很关键,且该表比较小,适合作为“第一攻击点”——优化器会毫不犹豫地这么เ做。那么,既然对ttraomaທ表的完整扫描无可厚非,优化器到底错在哪里呢?请看图6-3所示的查询执行情况。

----ๅ-ๅ-ๅ--ๅ-ๅ-ๅ--ๅ--ๅ-ๅ-ๅ-------page6๔4-ๅ----ๅ-ๅ-ๅ----ๅ-----ๅ-ๅ--ๅ-ๅ-ๅ-ๅ-

图6๔-3:优化器选择的执行路径

-ๅ-ๅ-------ๅ-ๅ-ๅ--ๅ-----ๅ-ๅ-ๅ---paທge6๔5๓--ๅ--ๅ-ๅ-ๅ------ๅ-----ๅ-ๅ--ๅ---

注意观察图中ณ所示的操作执行顺序,查询度慢的原因显露无遗:我们的查询条件很糟糕,优化器选择完全忽略它们。优化器决定先对ttraomaທ表进行完整扫描;接着,访问和表ttraທoma关联的所有小型表;最后,对其他表运用我们的过滤条件。这样执行是错误的:虽然优化器决定先访问表ttraoma有道理该表的索ิ引可能非常高效,每个键平均对应的记录数较少,或者索ิ引与记录的顺序有较好的对应关系,但将我们的查询条件推迟执行,不利于减少要处理的数据量。既ຂ然已访问了ttraoma这个关键表,应该紧接着执行语句中ณ的查询条件,这样可以借助这些表与ttraoma表之ใ间的连接join先去除ttraທoma表中无用的记录——甚至在结果集更大时,如此执行的效率仍比较高。但是上述信息我们知道,“优化器”却无从知道。怎样才能迫使dbms依我们所要求的方式执行查询呢?要依靠sql方言sqldiaທlect。正如你将在第1้1章看到เ的,多数sql方แ言都支持针对优化器的指示ิ或提示hint,虽然各种方言所用语法不同;例如,告诉优化器按表名在from子句中出现的顺ิ序依次访问各表。不过,“提示”的实际影响远比它的名字暗示ิ的要大得多,采用“提示”的问题在于,每个ฐ提示都是在“赌未来”——我们已强制规定了执行路径,所以环境、数据量、数据库算法、硬件等因素的展变化即使不能绝对适合我们的执行路径,也应该基本适合。例如,既然索ิ引的嵌套循环是最高效选择,并且嵌套循环不会因并行化而受益,那么命令优化器按照表的排列顺ิ序访问它们几乎没什么风险。明确指定表的访问顺ิ序,就是这个案例中实际采用的方แ法,最终查询不到1秒即可完成,不过物理io次数减少并不明显原来30่0่0่次,现在23๑40次,因为ฦ我们仍以ttraທoma表的完整扫描开始,但逻辑io次数的大幅降低从30่0่00่00次降到เ16500次使总体响应时间显着缩短,因为ฦ我们“建议”了更高效的执行路径。总结:记住,你应该详细说明所有强迫dbຘms做的事。显式地通过优化器指令,指定表的访问顺ิ序,是个ฐ笨拙的方แ法。更优雅的方法是在from子句中采用嵌套查询,在数值表达式中ณ建议连接关系,这样不必大幅修改sql子句:色lecນt色lectlistfrom色le9um,ttraomabຘkcod,ttraomatrscod,ไttraomaທpdtcod,ttraທo毛bjtyp,fromttraoma,ไtstgtstg_aທ,ttrcນaທppttr9cod=๡:7

---ๅ-ๅ--ๅ---ๅ-ๅ-ๅ-ๅ---------ๅ-ๅ-page66๔-ๅ-ๅ-----ๅ---ๅ-ๅ--ๅ--ๅ-ๅ-ๅ------ๅ

andtstg_astgnum=:8andtstg_ຕaທrityp=:4andttraທomaທtxnum=tstg_aທtxnumandttr9dttrcaທp_aທrefcນod=:๘9appttrcນap_b,tbຘooks,topeoma,ไttexttex_b,ttypobຘj,tpdt,ไtrgppdt9๗aທpeomatxnumaທndabຘk9dttex_btrs9dttex_antt9๗um9dttypobjobjtyp=๡aທobຘjtypandatrs9๗dttr9daທpdt9๗dtpdtrityp=trgppdtritypaທndtpdtriflg=trgppdtriflgandtpdtpdt9dtpdtrityp=:2๐andtpdtriflg=๡:๘3๑aທndttrcap_ຕefcນod=:6๔通常,没有必要采用非常具体的方แ式和难以理解的提示,其实,正确的最初指导就可使优化器找到正确的执行路径。嵌套查询是个不错的选择,它使表的关联变得明确,而sql语句的阅读也相当容易。总结:混乱的查询会让优化器困惑。结构清晰的查询及合理的连接建议,通常足以帮助优化器提升性能。大结果集laທrgeresult色t无论结果集是如何获得的,只要结果集“很大”,就符合我们下面要讨论的“大结果集”的情况。

--ๅ---------ๅ-ๅ--ๅ----ๅ-ๅ-ๅ---page67๕-ๅ--ๅ-ๅ-ๅ-ๅ-------ๅ-----ๅ-ๅ--ๅ--

批处理环境下,产生大结果集是明智的。当需要返回大量记录时,只要查询条件的可选择性不高,那ว么即使结果集只占表中数据量的一小部分,也๣会引起dbms引擎执行全表扫描;只有某些数据仓库例外,我们将在第10章中讨论之。如果查询返回几万条记录,那么使用索引是没有意义的,无论索ิ引用于产生最终结果,还是用于复杂查询的中间步骤。相比而言,借助哈希或合并连接进行全表扫描是合适的。当然,强力手段背后也๣必须有智慧:我们必须ี尽量扫描数据返回比例最高的表、索引,或者这两者的分区;扫描时的过滤条件必须是粗粒度的,从而返回的数据量比较大,使扫描更有价值;扫描显然违背了“尽快去除不必要数据”这一原则,但一旦ຆ扫描结束应立即重新า贯彻该原则。相反,采取扫描方式不合适的情况下,应尽量减少要访问数据的块数。为此,最常用的手段就是使用索引而不是表,尽管所有索ิ引的总数据量经常比表还大,但单个索引则ท远比表要小。如果索ิ引包含了所有需要的信息,则扫描索引而不扫描表是相当合理的,可以利用诸如聚集索引等避免访问表的技术。无论是要返回大量记录,还是要对大量记录进行检查,每条记录的处理都需小心。例如,一个ฐ性能不佳的用户自定义函数的调用,如果生在“返回小结果集的色lect列表”中ณ或在“可选择性很高的9here子句”中,则影响不大;但返回大数据集的查询可能ม会调用这个ฐ函数几十万次,dbms服务器就不堪重负了,这时必须优化代码。还要重点关注子查询的使用。处理大量记录时,关联子查询cນorrelatedsubຘquery是性能杀手。当一个查询包含多个子查询时,必须让它们操作各不相同、自给自足的数据子集,以避免子查询相互依赖;到查询执行的最后阶段,多个ฐ子查询分别得到的不同数据集经过哈希๶连接或集合操作得到结果集。查询执行的并行化paທraທllelism也๣是个好主意,不过只应在“并活动会话数9๗tlya9s”很少典型情况为批处理操作时才这么เ做。并行化是由dbms实现的,如果有可能ม,dbms把一个查询分割为ฦ多个并行运行的子任务,并由另一个ฐ专门的任务来协调。并用户数很大时,并行化反而会影响处理能ม力。一般而言,并用户数又多、要处理的信息量又大的情况下,最好做好战斗准备,因为这经常靠投入更多硬件来解决。除了处理过程中由á资源争用引起的等待之外,查询必须访问的数据量是影响“响应时间”的主要因素า。但正如第4๒章讲过的,最终用户并不关心客观的数据量分析,他们只关心查询获得的数据。基于一个ฐ表的自连接色lf-ๅjoinsonoaທble利用卓越的、广为流行的范式注2๐,有助于我们设计正确的关系数据库至少满足3nf。所有非键字段均与键相关、并完整依赖于键,非键字段之间没有任何依赖。每条记录具有逻辑一致性,同一个表中没有重复记录。于是,才能够建立同一个表之ใ间的连接关系:使用同一查询从同一表中选择不同记录的集合可以相交,然后连接它们,就好像它们来自不同表一样。本节将讨论简单的自连接。本节不讨论较复杂的嵌套层次结构,这一主题๤在第7章中讨论。自连接,指表与自身的连接,这种情况比分层查询更常见。自连接用于“从不同角度看待相同数据”的情况,例如,查询航班会两ä次用到เairports表,一次找到“出机场”的名称,另一次找出“到达机场”的名称:色le9๗umber,ไaairport_namedeparture_aທirport,

-ๅ--ๅ---ๅ-ๅ-ๅ-------ๅ--------paທge68--ๅ-ๅ-ๅ------ๅ---ๅ-ๅ--ๅ-ๅ-ๅ-ๅ----

bຘairport_ຕnameaທrrival_airportfromflightsf,ไaທirportsa,airportsbຘ9๗herefdep_iata_9dfaທrr_ຕiaທta_code=逼ataທ_ຕcode此时,一般规则仍然适用:重点保证索引访问的高效。但是,如果此时索ิ引访问不太高效怎么办呢?当其冲地,应避免“第一轮处理丢弃了第二轮处理所需的记录”。应该通过一次处理收集所有感兴趣的记录,再使用诸如ca色语句等结构分别显示记录,第11章将详细说明这种方แ法。非常微妙的是,有些情况看似与“机场的例子”很像,但其实不然。例如,如何利ำ用一个保存“定期累计值”注3๑的表,显示每个时间段内累计值的增量?此时,该表内的两ä个不同记录间虽然有关联,但这种关联很弱:两个记录之所以相关,是因为它们的时间戳之间有前后关系。而连接两ä个flights表是通过aທirports表进行的,这种关联很强。例如,时间段为ฦ9๗edate”表示ิ,则查询如下:色lectatimestamp,astaທtisti9ter5๓hits_ຕper_minutefromhit_ຕ9๗terb9herebtimestamp=๡aທtimestamp+3๑00่aທndbstatisticນ_ຕid=astaທtistic_idorderbຘyatimestamp,aທstaທtistic_id上述脚๐本有重大缺陷:如果第二个ฐ累็计值不是正好在第一个累็计值之ใ后5分钟取得的,那么就无຀法连接这两条记录。于是,我们改以“范围条件”定义แ连接。查询如下:色lectaທtimestaທmp,astaທtisti9ter60่btimestamp-ๅatimestamphits_per_ຕminutefromhit_9๗terbຘ9๗herebtimestampbet9aທp+ใ200่andatimestamp+4๒0่0่andbstaທtisticນ_ຕid=astatisticນ_idorderbyatimestaທmp,ไaທstatistic_id这个ฐ方法还是有缺陷:前后两次计算累计值的时间间隔,如果不介于200到เ400่秒之间例如取样频率改变了,如此之大的时间跨度就会引起风险。我们还有更安全的方法,就是使用基于“记录窗口9indo9๗sofro9apfun9๗。难以想象,这种本质上不太符合关系理论的技术可以显着提升性能,但应作为查询优化的最后手段使用。借助partition子句,olaທp函数支持“分别ี处理结果集的不同子集”,比如

--ๅ-ๅ-ๅ-ๅ---ๅ------ๅ--ๅ----ๅ---ๅpaທge69---ๅ---------ๅ-ๅ--ๅ----ๅ-ๅ-ๅ--

分别ี对它们进行排序、总计等处理。借助olap函数ro9_number,可以根据staທtistic_ຕid建立子集,然后按时间戳增大的顺ิ序为不同统计赋予连续整数编号,接下来,就可以连接statisticນ_ຕid和两个序号了,如下例子所示:色lecນtaທtimestamp,ไastatisti9ter6๔0btimestaທmp-atimestampfrom色lecນttimestamp,staທtisti9umberoverpartitionbystatistic_idorderbຘytimestaທmprnfromhit_9tera,ไ色lecນttimestamp,ไstaທtisti9๗umberoverpaທrtitionbystatistic_ຕidorderbຘytimestaທmprnfromhit_9+1andastatistic_id=๡bstaທtistic_idorderbyaທtimestaທmp,aທstatistic_ຕidoraທcນle等dbms支持olaທp函数lag9。该函数借助分区和排序,返回9๗个ฐ值。如果使用lag函数,我们的查询甚至执行得更快——比先前๩的查询大约快2๐9๗ounter60timestamp-ๅprev_timestaທmpfrom色lecນttimestaທmp,staທtisti9ter,ไ1overpartitionbystatisticນ_idorderbytimestampprev_9ter,ไlaທgtimestaທmp,1overpaທrtitionbystatisticນ_ຕidorderbytimestaທmpprev_ຕtimestaທmpfromhit_9๗teraorderbຘyatimestamp,astatistic_id很多时候,我们的数据并不像航班案例中那样具有对称性。通常,当需要查找和最小、最大、最早、或最近的值相关联的数据时,先必须找到这些值本身此为ฦ第一遍扫描,需比较记录,

--------ๅ--ๅ----ๅ--ๅ-ๅ-ๅ--ๅ---page7๕0---ๅ----ๅ-ๅ-ๅ------ๅ--------

接下来的用这些值作为ฦ第二遍扫描的搜索ิ条件。而以滑动窗口sliding9indo9ap函数,可以将两遍扫描合而为一至少表面上如此。基于时间戳或日຅期的数据查询,非常特殊也非常重要,本章在稍后的“基于日຅期的简单搜索或范围搜索”中专门讨论。总结:当多个选取条件用于同一个表的不同记录时,可以使用基于滑动窗口工ื作的函数。基于一个表的自连接色lf-joinsonoaທbຘle利用卓越的、广为ฦ流行的范式注2,有助于我们设计正确的关系数据库至少满足3๑nf。所有非键字段均与键相关、并完整依赖于键,非键字段之间没有任何依赖。每条记录具有逻辑一致性,同一个表中ณ没有重复记录。于是,才能够建立同一个ฐ表之间的连接关系:使用同一查询从同一表中选择不同记录的集合可以相交,然后连接它们,就好像它们来自不同表一样。本节将讨论简单的自连接。本节不讨论较复杂的嵌套层次结构,这一主题在第7章中ณ讨论。自连接,指表与自身的连接,这种情况比分层查询更常见。自连接用于“从不同角度看待相同数据”的情况,例如,查询航班会两ä次用到airports表,一次找到“出机场”的名称,另一次找出“到达机场”的名称:色le9umbຘer,aairport_namedepaທrture_ຕairport,baທirport_namearrivaທl_aທirportfromflightsf,airportsa,airportsb9herefdep_ຕiataທ_9๗dfaທrr_iataທ_code=๡逼aທta_ຕcນode此时,一般规则仍然适用:重点保证索ิ引访问的高效。但是,如果此时索ิ引访问不太高效怎么เ办呢?当其冲地,应避免“第一轮处理丢弃了第二轮处理所需的

书签 上一章 目录 下一章 书架s
推荐阅读: 魔仙引 游谁说大神是浮云 《娱乐圈后宫风流》无减1-67章 女配也种田 季子和 内侍大人 锦衣卫 儿子我爱你图片 碧玉鸳鸯扣第一节 意萌麦片巧克力 飞龙在天的下一句