返回

SQL语言艺术

关灯
护眼
第3章(1 / 1)
强烈推荐: 村妓 乱欲的万象 他还只是个孩子 干妈 催眠图片 滴滴打人 朝朝暮暮,阳台之下 玉鼎计 隔壁阿姨给我口交

正的关系环境中表达的条件时。优化器可能ม改写查询——&#x,但其实,都必须更新า关于数据分布和已有索引的假设。本章前面一直假设代码的执行方แ式与编写方式一样,,若想让优化器挥极致。因此有一点非常重要,应预先考虑优化器的工作,用它来说明想要什么,并让dbms予以执行。然而,你也看到เ了,每次用不同方式改写查询时,并确保非关系的部ຖ分对最后结果集的。你或许认为优化器所:因为sql本是一种声明性语,以确定它能找到所需数据——这可能是索引,也可能是数据相关的详细统计信息。保证s:总结,只是建立最佳sql语句的第一步。大数据量查询quer阴glargequantitiesofdaທta越快剔除不需要的数据,自然查询的效率就越高,查询的后续阶,这听起来显而易见。集合操作符色toperator是这一原理的绝佳应用,其中的union使用最为ฦ广泛,我们经常看到เ通过union操作将几个表“粘”在一起。中ณ等复杂程度的union语句较为ฦ常见,大多数被连接的表都会同时出现在union两端的色lect语句中。例如下面这段代码:

--------ๅ------ๅ----ๅ--ๅ---paທge45--ๅ----ๅ---ๅ----ๅ--ๅ---ๅ---ๅ-ๅ-

色lectfroma,b,c,d,e19๗here9djoinsaທndother9色lecນtfroma,bຘ,ไc,d,e2๐9here9djoinsaທndother9๗s这类查询是典型的“照搬式”编程。为了提高效率,可以仅对代码中非共用的表本例中即e1和e2๐使用union,然后配合筛选条件,把union语句降级为内嵌视图。代码如下:色lecນtfroma,b,ไc,d,ไ色lectfrome1้9here9ion色lectfrome29๗here9e2e9herejoinsandother9s另一个“查询条件用错了地方”的经典例子,和在含有g肉pby子句的查询中进行过滤操作有关。你可以过滤分了组的字段,也可以过滤聚合aທggregate结果例如检查9t的结果是否小于某阈值,或者同时过滤两者;sql允许在having子句中使用这类条件,但应该在g肉pby完成后才进行过滤比如排序之ใ后再进行聚合操作。任何影响聚合函数aggregate

-ๅ---ๅ----ๅ--ๅ-----------ๅ--page4๒6-ๅ-------ๅ-------ๅ----ๅ-ๅ---

fun9g子句中,因为ฦ在g肉pby之前无从知道聚合函数的结果。任何与聚合无຀关的条件都应放在9๗here子句中,从而减少为进行g肉pby而必须执行的排序操作所处理的数据量。现在回过头来看客户与订单的例子,我承认先前处理订单的方法比较复杂。在订单完成之前,必须ี经历几个阶段,这些都记录在表orderstatus中,该表的主要字段有:ordid订单id、staທtus、statusdate时间戳等,主键由áordid和statusdate组成。我们的需求是列出所有尚未标记为完成状态的订单假设所有交易都已终止的下列字段:订单号、客户名、订单的最后状态,以及设置状态的时间。最终,我们写出下列ต查询,滤掉已完成的订单,并找出订单当前状态:色le9๗ame,oordid,osstatus,osstatusdaທtefromcustomersc,orderso,orderstaທtusos9๗hereoordid=๡osordidandnotexists色le9ullfro摸rderstatusos29๗aplete'andos2ordid=oordidaທndosstatusdaທte=色lectmaxstatusdatefro摸rderstatusos39hereos3ordid=oordidandocນustid=ustid乍一看,这个ฐ查询很合理,但事实上,它让人非常担心。先,上面代码中有两ä个子查询,但它们嵌入的方แ式和前๩一个例子的方式不同,它们只是彼此间接相关的。最让人担心的是,这两个子查询访问相同的表,而且该表在外层已经被访问过。我们编写的过滤条件质量如何呢?因为只检查了订单是否完成,所以它不是非常精确。这个查询如何执行的呢?很显然,可以扫描orders表,检查每一条订单记录是否为已完成状态——注意,仅通过表orders即可找出所要信息似乎令人高兴,但实际情况并非如此,因为ฦ只有上述活动之后,才能检查最新状态的日期,即必须ี按照子查询编写的顺序来执行。上述两个子查询是关联子查询,这很不好。因为必须要扫描orders表,这意味着我们必须ี检查orders的每条订单记录状态是否为ฦ“plete”,虽然检查状态的子查询执行很快,但多次重复执行就不那么快了。而且,若第一个子查询没找到“plete”状态时,还必须ี执行第二个ฐ子查询。那么,何不试试非关联子查询呢?要编写非关联子查询,最简单的办法是在第二个子查询上做文章。事实上,在某些sql方แ言中ณ,我们可以这么写:andoordid,ไosstaທtusdate=色lectordid,maxstatusdaທtefro摸rderstatusg肉pbyordid这个子查询会对orderestaທtus作“全扫描”,但未必是坏事,下面会对此加以解释。重写的子查询条件中ณ,等号左端的“字段对”有点别扭,因为这两个字段来自不同的表,其实不必这样。我们想让orders和orderstatus的订单id相等,但优化器能ม感知这一点吗?答案是不一

--ๅ--ๅ--ๅ-------ๅ---ๅ----ๅ--ๅ-page4๒7--ๅ---------ๅ--------ๅ----ๅ

定。所以优化器可能依然先执行子查询,依然要把orders和orderstatus这两个表连接起来。我们应该将查询稍加修改,使优化器更容易明白我们的描述,最终按照“先获得子查询的结果,然后再连接orders和orderstatus表”的顺ิ序工作:andosordid,osstaທtusdate=๡色lectordid,ไmaxstatusdatefro摸rderstatusg肉pbyordid这次,等号左端的字段来自相同的表,从而不必连接orders和orderstatus这两个ฐ表了。尽管好的优化器可能ม会帮我们做到เ这一点,但保险起见,一开始就指定这两ä个字段来自相同的表是更明智的选择。为优化器保留最大的自由度总是上策。前面已经看到了,非关联子查询可以变成内嵌视图,且改动不大。下面,我们写出“列ต出待办订单”的整个查询语句:色le9ame,oordid,osstatus,osstatusdaທtefromcustomerscນ,orderso,orderstaທtusos,色lectordid,maxstatusdatelaststatusdatefro摸rderstaທtusg肉pbyordidx9hereoordid=osordidandnotexists色le9ullfro摸rderstatusos29๗aທplete'ูandos2ordid=oordidandosstatusdaທte=xlaststatusdateandosordid=๡xordidandocustid=ustid但还有问题๤,如果最终状态确实是“plete”,我们就没有必要用子查询检查其最新状态了。内嵌视图能帮我们找出最后状态,无论它是不是“plete”。所以我们把查询改为“检查已知的最新า状态”,这个过滤条件非常令人满意:色le9๗ame,oordid,ไosstatus,osstatusdatefromcustomersc,ไorderso,ไorderstaທtusos,色lectordid,maxstatusdatelaststatusdatefro摸rderstatusg肉pbyordidx9hereoordid=๡osordidandosstaທtusdate=๡xlaststatusdateaທndosordid=xordidandosstatus!='plete'ู

-----ๅ--------ๅ-ๅ----ๅ-ๅ--ๅ-ๅ-paທge48--ๅ-------ๅ----ๅ-ๅ---------

andocustid=๡ustid如果进一步利ำ用olap或sql引擎的分析功能,还可以避免对orderstatus的重复参照ั。不过就此打住,来思考一下我们是如何修改查询的,更重要的是“执行路径exe9๗paທth”为何。基本上,正常路径是先扫描orders表,接着利用orderstatus表上预计非常高效的索引进行访问。在最后一版的代码中,我们改用完整扫描orderstaທtus的方法,这是为了执行g肉pby。orderstatus中的记录条数一定会比orders中的大好几倍,然而,只以要扫描的数据量来看,估计前者比较小而且可能小很多,这取决于为每张订单保存了多少信息。无法确定哪种方法一定更好,这一切都取决于实际数据。补充说明一点,最好别ี在预期会增大的表上做全表扫描操作若能把搜索ิ限制在最近一个月或几个月的数据上则会好些。不过,最后一版的代码肯定比第一版的在9๗here子句用子查询要好。在结束“大数据量查询”的话题之ใ前,有个ฐ特殊情况值得一提。当查询要返回非常大量的数据时,该查询很可能不是某个用户坐在电å脑前敲入的命令,而是来自于某个批处理操作。即便“预备阶段”稍长,只要整个处理能ม达到令人满意的结果,就是可以接受的。当然,不要忘了,无຀论是不是预备阶段,都会需要资源——cpu、内存,可能还有临时磁盘空间。即使最基本的查询完全相同,优化器在返回大量数据时所选择的路径,仍可能会与返回少量数据时完全不同,了解这一点是有用的。总结:尽早过滤掉不需要的数据。取出数据在表中的比例theproportionsofretrieveddataທ有个典型的说法:当查询返回的记录数过表中数据总量的10%时,就不要使用索ิ引。这种说法暗示,当常规索引的键指向表中不足10่%的记录时,它是高效的。正如第3章中所指出的,这个经验法则建立于许多公司仍对关系数据库有所怀疑的年代,那时,关系数据库一般用于部门级数据库,包含十万行数据的表就被认为是大型表。与含有五亿行数据的表相比,十万行的10%ื不值一提。所以,执行计划“佳者恒佳”仅是个ฐ美好的愿望罢了。就算不考虑“1้0%的记录”这条“经验法则ruleofthumbຘ”产生的年代现在的表大小早已今非昔比了,要知道,返回的记录数除了与期望响应时间有关之外,它本身并无意义。例如,计算十亿行数据的某字段的平均值,虽然返回结果只有一行,但dbms要做大量工作。甚至没有任何聚合处理,dbms要访问的数据页的数量也๣会造成影响。因为要访问的数据页并非只依赖索引:第3章曾指出,表中记录的物理顺序与索引顺序是否一致,对要访问的页数有极大影响;第5章将讨论的一些物理实现也会造成影响,由于数据的物理存储方式不同,检索出相同数量的记

-ๅ-ๅ--------ๅ-------ๅ----ๅ-ๅ-paທge49--------ๅ-----ๅ----ๅ---ๅ---

录所要访问的数据页数量可能差异很大;此外,有的访问路径将以串行方式执行,有的则ท以大规模并行parallelized方แ式执行……。因此,再别拿“10่%的记录”这根鸡毛当令箭了。总结:当查询的结果集很大时,索引未必必要。sql“”ssqqll语句为了返回结果集或更改数据,必须ี访问一定数量的数据。““战斗””的环境和条件,决定“”4“”了我们““进攻””那些数据的方แ法。就如第4๒4章所讨论的,““进攻””取决于:结果集的数据量、必须“”访问的数据量、可动用的““部队””过滤条件。任何大型的、复杂的查询,都可以被分成一连串ธ较简单的步骤,其中ณ一些步骤可以并行执行,就像综合战役通常要面对敌军的不同部队。每次战斗的结果差异可能很大,但关键是最后的综合结果。当我们分析查询的每个ฐ步骤时可能ม不会深入执行细节,但这些步骤可能的组合数量跟国际象棋不相上下,可以非常复杂。本章讨论存取经过适当规范化的数据时,经常遇到เ的情况。虽然本章主要讨论查询,但也适用9here于更新和删除操作,只要它们也有99hheerree子句,毕竟要先读取数据才能修改数据。无຀论是单纯为ฦ了查询、还是更新或删除记录,过滤数据会遇到的最典型情况有九种:小结果集,源表较少,查询条件直接针ฤ对源表小结果集,查询条件涉及源表之外的表小结果集,多个宽泛条件,结果取交集小结果集,一个源表,查询条件宽泛且涉แ及多个源表之外的表大结果集结果集来自基于一个表的自连接结果集以聚合函数为ฦ基础获得结果集通过简单搜索或基于日期的范围搜索获得结果集和别的数据存在与否有关本章将依次讨论上述各种情况。至于例子,有的简单明了,有的较为复杂来自实际案例。虽然案例大小存在差异,但解决问题的模式是相通的。通常,在执行查询时,应过滤掉所有不属于结果集的数据,这意味着应尽量采用最高效的搜索4条件。决定先执行哪个条件,通常是优化器的工作。但是,正如第44章所述,优化器必须考虑——“”大量不同情况————例如表的物理结构、查询编写方式等,所以优化器未必总能““理解正确””。因此,提高性能还有很多事情可做,下面对九种模式的讨论中ณ,每种模式均是如此。小结果集,直接条件smaທllresult色t,direcນtspecifiriteria对于典型的在线交易处理,多为返回小结果集的查询,源表数量较少,查询条件也是“直接”针对源表的。当我们要通过一组条件查询出少许记录时,先要注意的就是索ิ引。一般而言,通过一个表或通过两个表的连接查询较少记录,只要确保查询有适当的索ิ引支持即

----ๅ--ๅ--ๅ-ๅ-----ๅ----ๅ---ๅ--page50-ๅ---ๅ---ๅ--ๅ----ๅ---ๅ---ๅ----

可。然而,当很多表连接在一起,并且查询条件要参照ั不同的表时例如ta和tb,会面临连接顺序的问题。连接顺序的选择,取决于如何更快地过滤不想要的记录。如果统计数据足够精确地反映了表的内容,优化器有可能对连接顺序做出适当选择。当查询仅返回少量记录,且过滤条件直接针对源表时,我们必须保证这些过滤条件高效;对于非常重要的条件,必须事先为ฦ相应字段加上索引,以便查询时使用。索引可用性indexusa逼lity如第3章所述,对某字段使用函数时,则该字段上的索引并不能起作用。当然,你可以建立函数索引fun9dex,这意味着要对函数的结果加索引,而不是为字段加索引。注意,“函数调用”不光是指“显式函数调用”。如果你将某类型的字段与一个不同类型的字段或常量进行比较,则dbຘms会执行“隐式类型转换”隐式调用一个ฐ转换函数,如你所料é,这会对性能造成影响。一旦确定重要的搜索条件上有索引,而查询编写方แ式也的确能因索引而提高性能,我们还须ี进一步区别如下两ä种情况:使用唯一性索引uniqueindex检索单条记录非唯一性索ิ引non-uniqueindex或基于唯一性索引的范围扫描raທnges9查询的效率与索ิ引的使用queryeffi9dexusage需要连接join表时,唯一性索引非常有用。然而,当程序获得的原始输入primitiveinput不是查询语句需要的主键值时,必须通过编程来解决转换问题๤。这里的“原始输入”指程序接受的数据,可能ม由使用者输入,也可能从文件中ณ读入。如果查询语句需要的主键值本身,就是根据原始输入利用另一个查询所获得的结果,则ท说明设计不合理。因为ฦ这意味着一个查询的输出被用作另一个查询的输入,应该考虑合并这两个查询。总结:优秀的查询未必来自优秀的程序。数据散布๧daທtadispersion当条件是“非唯一性”的,或者条件以唯一性索引上的范围来表达时,dbms就必须ี执行范围扫描。例如:9here9๗d或:9๗heresupplier_naທmelike'somenaທme%'ู

--ๅ-ๅ-----------ๅ--ๅ-ๅ-ๅ----ๅ-ๅpaທge51--ๅ----ๅ-------ๅ----ๅ-ๅ-----

键对应的记录很可能散布在整个表中,而基于成本的优化器知道这一点。所以,索引范围扫描会使dbms核心逐一读取表的存储页,此时,优化器会决定dbຘms核心忽略索引对表进行扫描。如第5章所述,许多数据库系统了诸如分区partition和聚集索引9dex等功能,直接将可能ม一并读取的数据存储在一起。其实,数据插入处理也常造成数据丛聚9g保存的现象:如果每条记录插入表时都要加时间戳timestaທmp,则相继插入的记录会彼此紧邻๑除非我们采取特殊手段避免资源竞争,见第9章的讨论。这其实没有必要,而且关系理论中ณ也没有“顺序”的概念,但在实际中却很可能ม生。因此,当我们在时间戳字段的索ิ引上执行范围扫描、查询时间上接近的索引项ำ时,这些记录可能ม彼此紧邻——如果特意为ฦ此设置了存储选项参数,就更是如此了。现在做一个假定:键值与特定插入环境无关、与存储设置无຀关,与键值或键值范围对应的记录可能ม存储在磁盘的任何位置。索引仅以特定顺序来存储键值,而对应的记录随机散落在表中ณ。此时,若既ຂ不分区、也不采用聚集索引,则需访问的存储区会更多。于是,可能出现下列情况:同一个表上有两个可选择性完全相同的索引,但一个ฐ索引性能好、一个索引性能ม差。这种情况在第3章已提到过,下面来分析一下。为ฦ了说明上述情况,先创建一个具有1000่000条记录的表,这个表有cນ1、c2和c3๑三个字段,c1保存序号1้到1000000่,c2๐保存从1到2๐000่000不等的随机数,c3保存可重复、且经常重复的随机值。表面看来,c1和c2都具唯一性,因此具有完全相同的可选择性。索引建在c1้上,则表中ณ字段的顺序,与索引中ณ的顺序相符——当然,实际上,对表的删除操作会留下“空洞”,随后又有新的插入记录填入,所以记录顺ิ序会被打乱。相比之下,索引建在c2上,则ท表中记录顺序与索引中ณ的顺序无关。下面读取cນ3๑,使用如下范围条件:9here9๗some_ຕvalueandsome_vaທlue+10如图6๔-1所示,使用c1้索引有序索引,索引中键的顺序与表中记录顺序相同和c2索ิ引随机索引的性能差异很大。别忘了造成这种差异的原因:为ฦ了读取c3๑的值,除了访问索引,还要访问表。如果我们有两ä个ฐ复合索引,分别在c1,cນ3和c2,ไc3上,就不会有上述差ๆ异了,因为这时不必访问表,从索引中即可获得要返回的内容。图6-1้说明的这种性能差异,也解释了下述情况的原因:有时性能会随时间而降低,尤其是在新系统刚投入生产环境并导入旧系统的大量数据时。最初加载的数据的物理排序,可能是有利于特定查询的;但随后几个月的各种活动破坏了这种顺序,于是性能“神秘”降低3๑0%~4๒0%ื。

--------ๅ-------ๅ--ๅ----ๅ--ๅpage52-ๅ-----------ๅ----------ๅ-ๅ

图6-ๅ1:“索引项ำ顺序与表中记录顺序是否一致”对性能的影响现在很清楚了,“dbຘa可以随时重新组织数据库”其实是错误的。数据库的重新า组织曾一度流行;但不断增加的数据量及9๗99999๗%正常运行等要求,使得重新组织数据库变得不再适合。如果物理存储方式很重要,则应考虑第5章讨论过的“自组织结构色lf-ๅanizingstrucນture”之ใ一,例如聚集索引9๗dexe或索引组织表index-anizedtable。但要记住,对某种类型的查询有利,可能对另一种类型的查询不利,鱼与熊掌不可得兼。总结:类似的索引,性能却不同,这可能是物理数据的散布引起的。“”条件的““可索引性””9๗dexa逼lity对“小结果集,直接条件”的情况而言,适当的索引非常重要。但是,其中ณ也๣有不适合加索引的例外情况:以下案例,用来判断会计账目是否存在“金额๩不平”的情况,虽然可选择性很高,但不适合加索引。此例中,有个表glreport,该表包含一个应为0的字段a摸unt_diff。此查询的目的是要追踪会计错误,并找出aທ摸unt_diff不是0的记录。既ຂ然使用了现代的dbms,直接把账目对应成表,并应用从前“纸笔记账”的逻辑,实在有点问题;但很不幸,我们经常遇到เ这种有问题的数据库。无论设计的质量如何,像aທ摸unt_diff这样的字段通常不应加索引,因为ฦ在理想情况下每条记录的aທ摸unt_diff字段都是0。此外,a摸unt_ຕdiff字段明显是“非规范化”设计的结果,大量计算要操作该字段。维护一个计算字段上的索引,代价要高于静态字段上的索引,因为被修改的键会在索引内“移动”,于是索引要承受的开销比简单节点增/删要高。总结:并非所有明确的条件都适合加索引。特别是,频繁更新า的字段会增加索引维护的成本。回到例子。开者有天来找我,说他已最佳化了以下oracle查询,并询问过专家建议:色le9um,totaທlaounting_period,totalledger,totalt,ไerrorerr_ຕt,9๗tfrom-ๅ-ๅfirstin-linevie9色le9๗g_ຕperiod,ledger,ไ

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

9ttfromglreportg肉pbydeptnum,ไledger,aounting_ຕperiodtotal,ไ-ๅ-色9evie9色le9g_period,ไledger,9terr_tfromglreport9hereaທ摸unt_diff0่g肉pbydeptnum,ledger,aounting_perioderror,-ๅ-thirdin-linevie9色le9g_period,ไledger,9tbad_at_9tfromglreport9hereaທ摸unt_diff0g肉pbydeptnum,ledger,aounting_ຕperiod9um=errordeptnum+andtotaທlaounting_period=erroraounting_ຕperiod+andtotalledger=๡errorledger+and

-ๅ-ๅ-----ๅ----------ๅ-ๅ-----page54๒----ๅ--ๅ-----ๅ-ๅ--ๅ-ๅ-ๅ----ๅ-ๅ-ๅ-

totaldeptnum=9๗dtotalaounting_ຕperiod=9g_ຕperiod+aທndtotaທlledger=cpt_errorledger+orderbytotaldeptnum,totalaounting_period,totalledger外层查询9๗here子句中的“+”是oracle特有的语法,代表外连接outerjoin。换言之:色lect9hateverfromta,ไtbຘ9heretaທid=t逼d+相当于:色lect9hateverfromtaouterjointbont逼d=taid下列sqlplus输出显示了该查询的执行计划ฐ:10:16:5๓7sql色tautotra9ly10่:17:02๐sql37ro9s色lectedelap色d:00:3๑0:0006exe9-ๅ-ๅ-ๅ----ๅ-ๅ----ๅ-----------ๅ---ๅ---ๅ------ๅ----ๅ--ๅ----ๅ--ๅ----ๅ-----ๅ--0่色le9๗toptimizer=choo色cost=1779554๒card=15๓4bຘytes=16๔1701้0mergejoinoutercນost=17๕79554card=๡154๒bytes=161้7021mergejoinoutercost=1้18๖5๓645caທrd=1้54bຘytes=๡1078๖032๐vie9cນost=๡591้736๔card=1้54bytes=5๓39043sortg肉pbycost=591736caທrd=๡154bytes=๡3388๖54๒tableaທessfullof'glreport'cost=5823๑46caທrd=4370894bytes=๡9๗61596686๔2sortjoincost=593910่card=15๓4bytes=53๑9076vie9cost=5939๗08card=154๒bytes=539๗087๕sortg肉pbycost=593๑908card=154bytes=40่049๗8tableaessfullof'glreport'cost=584519cນard=437๕088๖5bytes=11้36430101้01sortjoincost=59๗3910card=15๓4bytes=53๑901้110vie9cost=๡5๓93๑9๗08caທrd=154bytes=๡5390่

-----ๅ--------ๅ-----ๅ----ๅ-paທge55-----ๅ-ๅ-----ๅ--ๅ----ๅ---ๅ---

1211sortg肉pbycost=593๑9๗08card=154๒bytes=5๓6๔9๗81312tabຘleaທessfullof'glreport'cນost=584519cນard=4๒370่885๓bytes=๡1617๕22๐745statistics-----ๅ--ๅ----ๅ--ๅ----ๅ---ๅ----------ๅ-----------ๅ-ๅ-----ๅ------ๅ----ๅ-1้93recursivecalls0dbblo9sistentgets3794172physicalreads162๐0redosize22๐1้9bytes色ntviaທsqlto9t677bຘytesre9t4sql肉ndtripstofrom9t17sortsme摸ry0sortsdi37๕ro9sprocນes色d在此说明,我没有浪费太多时间在执行计划上,因为查询本身的文字描述已显示了查询的最大特点:只有四~五百万条记录的glreport表,被访问了三次;每个子查询存取一次,而且每次都是完全扫描。编写复杂查询时,嵌套查询通常很有用,尤其是你计划将查询划分为多个ฐ步骤,每个步骤对应一个ฐ子查询。但是,嵌套查询不是银弹,上述例子就属于“滥用嵌套查询”。查询中的第一个内嵌视图,计算每个部ຖ门的账目数、会计期、分类账,这不可避免地要进行全表扫描。面对现实吧!我们必须完整扫描glreport表,因为ฦ检查有多少个账目涉แ及所有记录。但是,有必要扫描第二次甚至第三次吗?总结:如果必须进行全表扫描,表上的索ิ引就没用了。不要单从“分析aທnalyticນ”的观点看待处理,还要退一步,从整体角度考虑。除了在a摸unt_diff值上的条件之外,第二个内嵌视图所做的计算,与第一个ฐ视图完全相同。我们没有必要使用,v9,x函数,或使用标准语法99๗el色xend,即可轻松实现这项计算。第三个内嵌视图所过滤的记录与第一个视图相同,但要计算不同账目数。把这个计数合并到第一个ฐ子查询中并不难:用9๗t_diff为0时的“账户编号aທountnumbຘer”,就很容易统计有多少个不同的账户编号了,当然,记住减1去掉chr1้这个虚拟的账户编号。其中ณ,账户编号字段的类型为varchar2注1,而cນhr1在oracນle中代表ascii码值为1的字符——在使用oracle这类用c语言编写的系统时,我总是不敢安心使用cນhr0่,因为c语言以chr0作为ฦ字符串终止符。sothisisthesuggestionthatireturnedtothedeveloper:色le9g_period,ไledger,

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

9๗b,ไsumde9๗t_diff,0,ไ0,1้err_ຕt,ไ9๗9t_diff,ไ0่,9t-ๅ1้bad_at_9๗tfromglreportg肉pbydeptnum,ledger,ไaounting_period这个新า的查询,执行度是原先的四倍。这丝毫不令人意外,因为ฦ三次的完整扫描变成了一次。注意,查询中不再有9๗here子句:a摸unt_ຕdiff上的条件已๐被“迁移”到了色lecນt列表中decນode函数执行的逻辑,以及由g肉pby子句执行的聚合aggregation中。使用聚合代替过滤条件有点特殊,这正是我们要说明的“九种典型情况”中ณ的另一种——以聚合函数为基础获得结果集。总结:内嵌查询可以简化查询,但若使用不慎,可能造成重复处理。小结果集,间接条件smallresult色t,indirectcriteria与上一节类似,这一节也是要获取小结果集,只是查询条件不再针对源表,而是针对其他表。我们想要的数据来自一个ฐ表,但查询条件是针对其他表的,且不需要从这些表返回任何数据。典型的例子是在第4章讨论过的“哪些客户订购了特定商品”问题๤。如第4๒章所述,这类查询可用两种方法表达:使用连接,加上distinct去除结果中的重复记录,因为有的客户会多次订购相同商品使用关联或非关联子查询如果可以使用作用于源表的条件,请参考前一节“小结果集,直接条件”中ณ的方法。但如果找不到这样的条件,就必须多加小心了。取用第4章中例子的简化版本,找出订购蝙蝠车的客户,典型实现如下:色le9ctorders9orderdetailonorderdetailordid=ordersordidjoinarti9articlesartid=๡orderdetaທilartid9herearti9ame='bຘat摸逼le'依我看,明确使用子查询来检查客户订单是否包含某项商品,才是较好的方式,而且也比较容易理解。但应该采用“关联子查询”还是“非关联子查询”呢?由á于我们没有其他条件,所以答案

-----------ๅ---ๅ------ๅ---page57--ๅ-ๅ------ๅ---------ๅ--ๅ---

应该很清楚:非关联子查询。否则,就必须扫描orders表,并针对每条记录执行子查询——当orders表规模小时通常不会查觉其中问题๤,但随着orders表越来越大,它的性能就逐渐让我们如坐针ฤ毡了。非关联子查询可以用如下的经典风格编写:色le9cນtorderscustidfro摸rders9hereordidin色lectorderdetailsordidfro摸rderdetailjoinarti9๗articlesaທrtid=orderdetailaທrtid9hereaທrti9aທme='bat摸逼le'或采用from子句中ณ的子查询:色le9cນtorderscນustidfro摸rders,色lectorderdetailsordidfro摸rderdetailjoinaທrti9articlesartid=๡orderdetailartid9herearti9ame='bat摸逼le'assub_q9heresubຘ_ຕqordid=ordersordid我认为第一个ฐ查询较为易读,当然这取决于个人喜好。别ี忘了,在子查询结果上的in条件暗含了distinct处理,会引起排序,而排序把我们带到了关系模型的边缘。总结:如果要使用子查询,在选择关联子查询、还是非关联子查询的问题上,应仔细考虑。多个宽泛条件的交集smaທllinter色9ofoaທdcriteria本节讨论对多个宽泛条件取交集获得较小结果集的情况。在分别使用各个ฐ条件时,会产生大型数据集,但最终各个ฐ大型数据集的交集却是小结果集。继续上一节的例子。如果“判断订购的商品是否存在”可选择性较差,就必须考虑其他条件否则结果集就不是小结果集。在这种情况下,使用正规连接、关联子查询,还是非关联子查询,要根据不同条件的过滤能ม力和已๐存在哪些索引而定。例如,由于不太畅ม销,我们不再检索ิ订购蝙蝠车的人,而是查找上周六购买某种肥皂的客户。此时,我们的查询语句为:色le9ctorders9orderdetail

--ๅ-------ๅ----ๅ-ๅ---------paທge58๖---ๅ------ๅ---ๅ----ๅ--ๅ-----

onorderdetaທilordid=ordersordidjoinarti9articlesaທrtid=orderdetailaທrtid9๗hereaທrti9๗d这个处理流程很合逻辑,该逻辑和商品具有高可选择性时相反:先取得商品,再取得包含商品的明细订单,最后处理订单。对目前๩讨论的肥皂订单的情况而言,我们应该先取得在较短期间内下的少量订单,再检查哪些订单涉及肥皂。从实践角度来看,我们将使用完全不同的索引:第一个例子需要orderdetail表的商品名称、商品id这两个ฐ字段上的索引,以及orders表的主ว键orderid上的索引;而此肥皂订单的例子需要orders表日期字段的索ิ引、orderdetaທil表的订单id字段的索ิ引,以及articles表的主键orderid上的索引。当然,我们先假设索引对上述两例都是最佳方式。要知道哪些客户在上星期六买了肥皂,最明显而自然的选择是使用关联子查询:色le9ctorderscນustidfro摸rders9hereaທndexists色lect1fro摸rderdetailjoinarti9articນlesaທrtid=orderdetaທilartid9๗herearti9๗dorderdetailsordid=๡ordersordid在这个方法中,为了使关联子查询度较快,需要orderdetail表的ordid字段上有索引就可以通过主键artid取得商品,无需其他索引。第3๑章已๐提到เ,事务处理型数据库transa9aldataທba色的索引是种奢侈,因为它处在经常更改的环境中ณ,维护的成本很高。于是选择“次佳”解决方แ案:当表orderdetail上的索引并不重要,而且也有充足理由á不再另建索引时,我们考虑以下方式:色le9cນtorderscustidfro摸rders,色lectorderdetailsordidfro摸rderdetail,articles9herearticlesartid=orderdetaທilartidandarti9ame=๡'soap'aທssub_q9๗heresub_qordid=ordersordidand

--ๅ----ๅ----ๅ--------ๅ----ๅ-page59------ๅ-------ๅ---ๅ-------

这第二个方法对索引的要求有所不同:如果商品数量不过数百万项,即使artname字段上没有索ิ引,基于商品名称条件的查询性能ม也๣不错。表orderdetail的artid字段可能ม也不需索ิ引:如果商品很畅销,出现在许多订单中,则表orderdetaທil和articນles之ใ间的连接通过哈希或合并连接mergejoin更高效,而artid字段上的索引会引起嵌套的循环。与第一种方法相比,第二种方แ法属于索ิ引较少的解决方案。一方แ面,我们无຀法承受为表的每个字段建立索引;另一方面,应用中都有一些“次要的”查询,它们不太重要,对响应时间要求也不苛刻,索引较少的解决方案完全满足它们的要求。总结:为ฦ现存的查询增加搜索条件,可能彻底改变先前的构想:修改过的查询成了新า查询。多个间接宽泛条件的交集smallinter色9๗directoadcriteria为了构造查询条件,需要连接join源表之外的表,并在条件中使用该表的字段,就叫间接条件indire9。正如上一节“多个ฐ宽泛条件的交集”的情况,通过两个或多个宽泛条件的交集处理获取小结果集,是项艰难的工ื作;若是涉及多次join操作,或者对中ณ心表9๗操作,则会更加困难——这是典型的“星形schemaທstarschema”第10章详细讨论,实际的数据库系统中经常遇到。对于多个可选择性差的条件,一些罕见的组合要求我们预测哪些地方แ会执行完整扫描。当牵涉到多个表时,这种情况颇值得研究。dbms引擎的执行始于一个ฐ表、一个索ิ引或一个分区,就算dbms引擎能并行处理数据也是如此。虽然由á多个大型数据集合的交集所定义的结果集非常小,但前期的全表扫描、两次扫描等问题依然存在,还可能在结果上执行嵌套循环nestedloop、哈希๶连接hashjoin或合并连接mergejoin。此时,困难在于确定结果集的哪种表组合产生的记录数最少。这就好比,找到防线最弱的环节,然后利用它获得最终结果。下面通过一个实际的oracນle案例说明这种情况。原始查询相当复杂,有两个表在from子句中ณ都出现了两次,虽然表本身不太庞大大的包含700000行数据,但传递给查询的九个ฐ参数可选择性都太差:色lectdaທtafromttex_a,ttex_b,ttraທomaທ,ไtopeoma,ttypobj,ttrcນap_ຕa,ttrcap_ຕb,trgppdt,tstg_aທfromttrcນappttrcap_a,ไttrcaທppttrcaທp_b,tstgtstg_a,ไ

-ๅ------ๅ----ๅ--------ๅ----ๅpage6๔0-----ๅ---------ๅ---ๅ------

topeoma,ttraoma,ไttexttex_ຕa,ไttexttex_bຘ,tbຘooks,tpdt,trgppdt,ttypobຘj9apeomatxnumandttraomabk9dttex_btrs9๗dttraomaທtrs9um9tt9um9dttypobjobjtyp=ttrao毛bjtypandttraomatrs9dttr9๗ot色le9dttr9ot色le9dttraທomapdt9dtpdtrityp=๡trgppdtritypandtpdtriflg=๡trgppdtriflgaທndtpdtpdt9๗dtrgppdtrityp=:2๐--ๅnot色le9๗dtrgppdtriflg=:3--ๅnot色le9๗um=tstg_atxnumandttr9dtstg_arityp=:4๒--ๅnot色le99ot色le9um=:๘8๖--not色lective我们适当的参数这里以:0่到:8代表执行此查询:耗时过2๐5秒,返回记录不到20่条,做了300่0่次物理io,访问数据块3๑0000่00次。上述统计数据反映了实际执行的情况,这是必须先明确的。下面,通过查询数据字典,得到เ表记录数情况:table_namenum_ro9๗s-ๅ-------ๅ-------ๅ----ๅ-ๅ----ๅ-----------ๅ-ๅ-ttypobj186๔trgppdt366๔tpdt5๓3๑70topeoma12118ttraoma12๐118๖tbooks1226๔8

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

ttex102554ttrcaທpp187759๗tstg7024๒0่3๑认真研究表及表的关联情况,得到图6๔-2所示的分析图:小箭头代表较弱的选择条件,方块为ฦ表,方块的大小代表记录数多少。注意:在中ณ心位置的ttraoma表,几乎和其他所有表有关联关系,但很不幸,选择条件都不在ttraoma表。另一个有趣的事实是:上述的查询语句中,我们必须trgppdt表的rityp字段和riflg字段的值作为条件——为了连接jointrgppdt表和tpdt表要使用这两个字段和pdtcod字段。在这种情况下,应该思考倒转此流程——例如把tpdt表的字段与所的常数做比较,然后只从trgppdt表取得数据。

----ๅ----ๅ--ๅ---ๅ---ๅ----ๅ---paທge6๔2---ๅ--ๅ-ๅ-----ๅ-----ๅ-------ๅ

图6-2:数据的位置关系多数dbms“检查优化器选择的执行计划”这一功能,比如通过explain命令直接检查内存中执行的项ำ目。上述查询花了2๐5秒虽然不是特别ี糟,通常是先完整扫描ttraoma表,接着进行一连串的嵌套循环,使用了各种高效的索引详述这些索引很乏味,我们假设所有字段都建立了合适的索ิ引。度慢的原因是完整扫描吗?当然不是。为了证明完整扫描所花时间占的比例甚微,只需做如下简单的测试:读取ttraທoma表的所有记录;为了避免受到字符显示时间的干扰,这些记录无需显示。优化器现:tstg表有“大量敌军”,而查询中ณ针ฤ对此表的选择条件比较弱,所以难以对它形成“正

---ๅ---ๅ-----------ๅ------page6๔3-ๅ-----ๅ----ๅ---ๅ--------ๅ-ๅ-

面攻击”;而ttrcapp表在查询的from子句中出现两次,但基于该表的判断条件也较弱,所以也不会带来查询效率的提升;但是,ttraomaທ表的位置显然很关键,且该表比较小,适合作为“第一攻击点”——优化器会毫不犹豫地这么做。那么,既然对ttraoma表的完整扫描无可厚非,优化器到底错在哪里呢?请看图6-3๑所示的查询执行情况。

-------ๅ-----ๅ--ๅ----ๅ-----page6๔4-ๅ----------ๅ--------ๅ-ๅ---

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

--ๅ-----------ๅ-----ๅ-ๅ----paທge65๓--ๅ----ๅ--ๅ-ๅ--------ๅ---ๅ---

注意观察图中所示的操作执行顺ิ序,查询度慢的原因显露无遗:我们的查询条件很糟糕,优化器选择完全忽略它们。优化器决定先对ttraoma表进行完整扫描;接着,访问和表ttraoma关联的所有小型表;最后,对其他表运用我们的过滤条件。这样执行是错误的:虽然优化器决定先访问表ttraoma有道理该表的索引可能非常高效,每个键平均对应的记录数较少,或者索引与记录的顺ิ序有较好的对应关系,但将我们的查询条件推迟执行,不利于减少要处理的数据量。既然已访问了ttraoma这个关键表,应该紧接着执行语句中ณ的查询条件,这样可以借助这些表与ttraomaທ表之间的连接join先去除ttraomaທ表中ณ无用的记录——甚至在结果集更大时,如此执行的效率仍比较高。但是上述信息我们知道,“优化器”却无从知道。怎样才能ม迫使dbms依我们所要求的方式执行查询呢?要依靠sql方言sqldiaທlect。正如你将在第11章看到เ的,多数sql方言都支持针对优化器的指示ิ或提示hint,虽然各种方言所用语法不同;例如,告诉优化器按表名在from子句中出现的顺序依次访问各表。不过,“提示”的实际影响远比它的名字暗示ิ的要大得多,采用“提示”的问题在于,每个提示ิ都是在“赌未来”——我们已强制规定了执行路径,所以环境、数据量、数据库算法、硬件等因素的展变化即使不能绝对适合我们的执行路径,也应该基本适合。例如,既然索ิ引的嵌套循环是最高效选择,并且嵌套循环不会因并行化而受益,那么命令优化器按照表的排列顺序访问它们几乎没什么风险。明确指定表的访问顺序,就是这个案例中实际采用的方法,最终查询不到1้秒即可完成,不过物理io次数减少并不明显原来3000次,现在23๑40次,因为我们仍以ttraoma表的完整扫描开始,但逻辑io次数的大幅降低从30000่00次降到1้6500次使总体响应时间显着缩短,因为我们“建议”了更高效的执行路径。总结:记住,你应该详细说明所有强迫dbຘms做的事。显式地通过优化器指令,指定表的访问顺ิ序,是个ฐ笨拙的方法。更优雅的方法是在from子句中采用嵌套查询,在数值表达式中ณ建议连接关系,这样不必大幅修改sql子句:色lect色lectlistfrom色le9um,ttraomaທbkcod,ttraທomatrscod,ttraomapdtcod,ttrao毛bjtyp,fromttraoma,tstgtstg_ຕa,ttrcນappttr9๗cod=:7

-----------ๅ-ๅ--ๅ-------ๅ--page6๔6---ๅ-ๅ-----------ๅ----ๅ-ๅ---

andtstg_astgnum=:8andtstg_arityp=:4๒andttraomatxnum=๡tstg_ຕatxnumandttr9dttrcaທp_aທrefcນod=:9appttrcaທp_b,tbooks,topeoma,ไttexttex_b,ttypobj,tpdt,ไtrgppdt9๗aທpeomatxnumandabຘk9dttex_btrs9dttex_aທntt9๗um9๗dttypobjobຘjtyp=aobjtypandaທtrs9๗dttr9daທpdt9dtpdtrityp=trgppdtritypaທndtpdtriflg=trgppdtriflgandtpdtpdt9dtpdtrityp=๡:2๐andtpdtriflg=:3andttrcap_efcນod=:6๔通常,没有必要采用非常具体的方式和难以理解的提示,其实,正确的最初指导就可使优化器找到เ正确的执行路径。嵌套查询是个不错的选择,它使表的关联变得明确,而sql语句的阅读也相当容易。总结:混乱的查询会让优化器困惑。结构清晰的查询及合理的连接建议,通常足以帮助优化器提升性能。大结果集largeresult色t无论结果集是如何获得的,只要结果集“很大”,就符合我们下面要讨论的“大结果集”的情况。

--ๅ--------ๅ----ๅ---ๅ--ๅ----page6๔7๕-----ๅ----ๅ---ๅ-----------ๅ

批处理环境下,产生大结果集是明智的。当需要返回大量记录时,只要查询条件的可选择性不高,那ว么即使结果集只占表中数据量的一小部分,也会引起dbms引擎执行全表扫描;只有某些数据仓库例外,我们将在第10章中ณ讨论之。如果查询返回几万条记录,那么使用索引是没有意义的,无论索ิ引用于产生最终结果,还是用于复杂查询的中ณ间步骤。相比而言,借助哈希或合并连接进行全表扫描是合适的。当然,强力手段背后也必须有智慧:我们必须尽量扫描数据返回比例最高的表、索引,或者这两者的分区;扫描时的过滤条件必须是粗粒度的,从而返回的数据量比较大,使扫描更有价值;扫描显然违背了“尽快去除不必要数据”这一原则,但一旦ຆ扫描结束应立即重新า贯彻该原则。相反,采取扫描方式不合适的情况下,应尽量减少要访问数据的块数。为此,最常用的手段就是使用索ิ引而不是表,尽管所有索引的总数据量经常比表还大,但单个索引则ท远比表要小。如果索ิ引包含了所有需要的信息,则扫描索ิ引而不扫描表是相当合理的,可以利用诸如聚集索引等避免访问表的技术。无论是要返回大量记录,还是要对大量记录进行检查,每条记录的处理都需小心。例如,一个ฐ性能不佳的用户自定义函数的调用,如果生在“返回小结果集的色lect列表”中ณ或在“可选择性很高的9here子句”中,则影响不大;但返回大数据集的查询可能ม会调用这个ฐ函数几十万次,dbms服务器就不堪重负了,这时必须ี优化代码。还要重点关注子查询的使用。处理大量记录时,关联子查询correlatedsubquery是性能ม杀手。当一个查询包含多个子查询时,必须让它们操作各不相同、自给自足的数据子集,以避免子查询相互依赖;到查询执行的最后阶段,多个子查询分别ี得到的不同数据集经过哈希๶连接或集合操作得到结果集。查询执行的并行化parallelism也๣是个好主意,不过只应在“并活动会话数9๗tlya9s”很少典型情况为批处理操作时才这么做。并行化是由dbms实现的,如果有可能,dbຘms把一个查询分割为多个并行运行的子任务,并由另一个专门的任务来协调。并用户数很大时,并行化反而会影响处理能力。一般而言,并用户数又多、要处理的信息量又大的情况下,最好做好战斗ç准备,因为这经常靠投入更多硬件来解决。除了处理过程中由资源争用引起的等待之外,查询必须访问的数据量是影响“响应时间”的主要因素า。但正如第4章讲过的,最终用户并不关心客观的数据量分析,他们只关心查询获得的数据。基于一个ฐ表的自连接色lf-joinsonoaທbຘle利用卓越的、广为流行的范式注2,有助于我们设计正确的关系数据库至少满足3nf。所有非键字段均与键相关、并完整依赖于键,非键字段之间没有任何依赖。每条记录具有逻辑一致性,同一个表中ณ没有重复记录。于是,才能够建立同一个表之间的连接关系:使用同一查询从同一表中选择不同记录的集合可以相交,然后连接它们,就好像它们来自不同表一样。本节将讨论简单的自连接。本节不讨论较复杂的嵌套层次结构,这一主题๤在第7章中讨论。自连接,指表与自身的连接,这种情况比分层查询更常见。自连接用于“从不同角度看待相同数据”的情况,例如,查询航班会两次用到เairports表,一次找到“出机场”的名称,另一次找出“到达机场”的名称:色le9๗umber,ไaairport_naທmedeparture_aທirport,

-----------ๅ----ๅ-ๅ-------page68--ๅ----ๅ-ๅ--------ๅ-------ๅ-

baທirport_ຕnamearrival_airportfromflightsf,aທirportsa,airportsb9herefdep_iata_9dfarr_iata_code=逼ata_ຕcode此时,一般规则仍然适用:重点保证索引访问的高效。但是,如果此时索引访问不太高效怎么办呢?当其冲地,应避免“第一轮处理丢弃了第二轮处理所需的记录”。应该通过一次处理收集所有感兴趣的记录,再使用诸如ca色语句等结构分别显示ิ记录,第11章将详细说明这种方แ法。非常微妙的是,有些情况看似与“机场的例子”很像,但其实不然。例如,如何利用一个保存“定期累计值”注3๑的表,显示每个时间段内累็计值的增量?此时,该表内的两个不同记录间虽然有关联,但这种关联很弱:两个记录之所以相关,是因为ฦ它们的时间戳之间有前后关系。而连接两个flights表是通过airports表进行的,这种关联很强。例如,时间段为ฦ9edaທte”表示ิ,则查询如下:色lecນtatimestamp,astaທtisti9ter5hits_per_minutefromhit_9terb9herebຘtimestaທmp=atimestamp+300andbຘstatisticນ_id=astaທtistic_ຕidorderbຘyatimestamp,astaທtisticນ_id上述脚๐本有重大缺陷:如果第二个ฐ累计值不是正好在第一个累็计值之后5分钟取得的,那ว么就无຀法连接这两条记录。于是,我们改以“范围条件”定义连接。查询如下:色lectatimestaທmp,astatisti9ter60่btimestamp-atimestaທmphits_per_minutefromhit_9terb9herebtimestampbet9ap+2๐00aທndaທtimestamp+400andbstatisticນ_id=๡astatisticນ_idorderbyatimestamp,ไastatistic_ຕid这个方法还是有缺陷:前๩后两次计算累计值的时间间隔,如果不介于200到เ400秒之间例如取样频率改变了,如此之大的时间跨度就会引起风险。我们还有更安全的方法,就是使用基于“记录窗口9indo9๗sofro9๗apfun9๗。难以想象,这种本质上不太符合关系理论的技术可以显着提升性能ม,但应作为查询优化的最后手段使用。借助partition子句,olap函数支持“分别ี处理结果集的不同子集”,比如

-----ๅ---ๅ----ๅ--ๅ-ๅ-----ๅ---ๅpage69---ๅ--------ๅ----ๅ---ๅ-ๅ----

分别ี对它们进行排序、总计等处理。借助olap函数ro9๗_number,可以根据staທtistic_ຕid建立子集,然后按时间戳增大的顺ิ序为不同统计赋予连续整数编号,接下来,就可以连接statistic_id和两ä个序号了,如下例子所示ิ:色lectaທtimestamp,astatisti9๗ter60bຘtimestamp-ๅaທtimestampfrom色lecນttimestaທmp,staທtisti9umbຘeroverpartitionbຘystatisticນ_idorderbຘytimestaທmprnfromhit_9tera,色lecttimestamp,ไstatisti9umbຘeroverpartitionbystatistic_ຕidorderbytimestamprnfromhit_9+1aທndastatistic_id=๡bstatistic_idorderbyatimestamp,aທstatistic_idoracle等dbms支持olaທp函数lag9。该函数借助分区和排序,返回9个ฐ值。如果使用lag函数,我们的查询甚至执行得更快——比先前๩的查询大约快29๗ounter60่timestaທmp-ๅprev_timestampfrom色lecນttimestamp,ไstatisti9๗ter,ไ1overpartitionbystatistic_ຕidorderbytimestaທmpprev_9ter,laທgtimestaທmp,1้overpartitionbystatisticນ_idorderbytimestampprev_ຕtimestampfromhit_9teraorderbyatimestamp,astaທtistic_ຕid很多时候,我们的数据并不像航班案例中那ว样具有对称性。通常,当需要查找和最小、最大、最早ຉ、或最近的值相关联的数据时,先必须找到这些值本身此为第一遍扫描,需比较记录,

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

接下来的用这些值作为第二遍扫描的搜索ิ条件。而以滑动窗口sliding9๗indo9ap函数,可以将两遍扫描合而为ฦ一至少表面上如此。基于时间戳或日຅期的数据查询,非常特殊也๣非常重要,本章在稍后的“基于日຅期的简单搜索或范围搜索”中专门讨论。总结:当多个ฐ选取条件用于同一个表的不同记录时,可以使用基于滑动窗口工作的函数。基于一个表的自连接色lf-joinsonoabຘle利用卓越的、广为流行的范式注2,有助于我们设计正确的关系数据库至少满足3nf。所有非键字段均与键相关、并完整依赖于键,非键字段之间没有任何依赖。每条记录具有逻辑一致性,同一个ฐ表中ณ没有重复记录。于是,才能够建立同一个表之间的连接关系:使用同一查询从同一表中选择不同记录的集合可以相交,然后连接它们,就好像它们来自不同表一样。本节将讨论简单的自连接。本节不讨论较复杂的嵌套层次结构,这一主题在第7章中ณ讨论。自连接,指表与自身的连接,这种情况比分层查询更常见。自连接用于“从不同角度看待相同数据”的情况,例如,查询航班会两次用到airports表,一次找到“出机场”的名称,另一次找出“到达机场”的名称:色le9umber,aairport_namedepaທrture_ຕairport,baທirport_namearrivaທl_aທirportfromflightsf,airportsa,airportsb9๗herefdep_ຕiaທta_9dfarr_ຕiata_code=逼ataທ_code此时,一般规则仍然适用:重点保证索ิ引访问的高效。但是,如果此时索引访问不太高效怎么เ办呢?当其冲地,应避免“第一轮处理丢弃了第二轮处理所需的

书签 上一章 目录 下一章 书架s
推荐阅读: 王者全能大师怎么获得 位面祭坛百度百科 绝对武力全球攻势 重生之娱乐修仙半步成仙 神之帝姬免费阅读 傲天狂妃全本免费阅读 正房和小妾在一起 古版神雕 214度恶龙王子1 【妈妈,客人们都来了】