第3章(1 / 1)

加入书签 本章报错

SQL语言艺术强烈推荐:

正的关系环境中ณ表达的条件时。若想让优化器挥极致,我们就必须扩大关系处理的工作量,并确保非关系的部分对最后结果集的影响最小。本章前面一直假设代码的执行方式与编写方แ式一样,但其实,优化器可能改写查询——有时改动还很大。你或许认为优化器所做的改写无关紧ู要,因为sql本是一种声明性语言de9guage,用它来说明想要什么,并让dbຘms予以执行。然而,你也看到了,每次用不同方式改写查询时,都必须更新า关于数据分布和已有索引的假设。因此有一点非常重要:应预先考虑优化器的工作,以确定它能找到เ所需数据——这可能ม是索引,也可能是数据相关的详细统计信息。总结:保证sql语句返回正确结果,只是建立最佳sql语句的第一步。大数据量查询quer阴glargequantitiesofdaທta越快剔除不需要的数据,查询的后续阶段必须处理的数据量就越少,自然查询的效率就越高,这听起来显而易见。集合操作符色toperator是这一原理的绝佳应用,其中的union使用最为ฦ广泛,我们经常看到เ通过union操作将几个表“粘”在一起。中ณ等复杂程度的union语句较为ฦ常见,大多数被连接的表都会同时出现在union两ä端的色lecນt语句中。例如下面这段代码:

--ๅ--ๅ----ๅ--ๅ-ๅ------ๅ------page45๓---ๅ------ๅ--ๅ-ๅ---ๅ--ๅ-ๅ-----

色lectfromaທ,ไb,c,d,ไe19here9djoinsaທndother9色lecນtfroma,bຘ,ไc,ไd,e2๐9here9djoinsaທndother9๗s这类查询是典型的“照搬式”编程。为ฦ了提高效率,可以仅对代码中非共用的表本例中即e1้和e2๐使用union,然后配合筛选条件,把union语句降级为内嵌视图。代码如下:色lecນtfromaທ,b,c,d,色lectfrome19here9๗ion色lectfrome29๗here9e2e9๗herejoinsandother9s另一个“查询条件用错了地方”的经典例子,和在含有g肉pby子句的查询中ณ进行过滤操作有关。你可以过滤分了组的字段,也可以过滤聚合aggregaທte结果例如检查9t的结果是否小于某阈值,或者同时过滤两者;sql允许在having子句中使用这类条件,但应该在g肉pbຘy完成后才进行过滤比如排序之后再进行聚合操作。任何影响聚合函数aggregate

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

fun9๗g子句中,因为在g肉pbຘy之前๩无从知道聚合函数的结果。任何与聚合无຀关的条件都应放在9๗here子句中,从而减少为进行g肉pby而必须执行的排序操作所处理的数据量。现在回过头来看客户与订单的例子,我承认先前处理订单的方法比较复杂。在订单完成之前,必须ี经历几个阶段,这些都记录在表orderstatus中,该表的主要字段有:ordid订单id、staທtus、statusdate时间戳等,主键由ordid和statusdaທte组成。我们的需求是列出所有尚未标记为完成状态的订单假设所有交易都已๐终止的下列字段:订单号、客户名、订单的最后状态,以及设置状态的时间。最终,我们写出下列查询,滤掉已完成的订单,并找出订单当前状态:色le9๗ame,oordid,osstatus,ไosstatusdaທtefromcustomersc,orderso,orderstaທtusos9๗hereoordid=๡osordidandnotexists色le9ullfro摸rderstaທtusos2๐9aplete'aທndos2ordid=oordidaທndosstatusdaທte=色lectmaxstaທtusdatefro摸rderstatusos3๑9๗hereos3ordid=oordidandocນustid=ustid乍一看,这个查询很合理,但事实上,它让人非常担心。先,上面代码中有两ä个子查询,但它们嵌入的方式和前一个例子的方แ式不同,它们只是彼此间接相关的。最让人担心的是,这两个ฐ子查询访问相同的表,而且该表在外层已经被访问过。我们编写的过滤条件质量如何呢?因为只检查了订单是否完成,所以它不是非常精确。这个查询如何执行的呢?很显然,可以扫描orders表,检查每一条订单记录是否为已๐完成状态——注意,仅通过表orders即可找出所要信息似乎令人高兴,但实际情况并非如此,因为只有上述活动之后,才能ม检查最新状态的日期,即必须ี按照ั子查询编写的顺序来执行。上述两ä个子查询是关联子查询,这很不好。因为必须要扫描orders表,这意味着我们必须检查orders的每条订单记录状态是否为“plete”,虽然检查状态的子查询执行很快,但多次重复执行就不那么เ快了。而且,若第一个子查询没找到“plete”状态时,还必须ี执行第二个子查询。那ว么,何不试试非关联子查询呢?要编写非关联子查询,最简单的办法是在第二个ฐ子查询上做文章。事实上,在某些sql方แ言中ณ,我们可以这么写:andoordid,ไosstaທtusdate=色lecນtordid,maທxstatusdaທtefro摸rderstatusg肉pbyordid这个子查询会对orderestaທtus作“全扫描”,但未必是坏事,下面会对此加以解释。重写的子查询条件中ณ,等号左端的“字段对”有点别扭,因为这两ä个字段来自不同的表,其实不必这样。我们想让orders和orderstaທtus的订单id相等,但优化器能ม感知这一点吗?答案是不一

----ๅ--ๅ-ๅ----ๅ-----ๅ--ๅ-ๅ---ๅ-page4๒7---ๅ--ๅ--ๅ-ๅ---ๅ------ๅ---ๅ---

定。所以优化器可能依然先执行子查询,依然要把orders和orderstatus这两个表连接起来。我们应该将查询稍加修改,使优化器更容易明白我们的描述,最终按照“先获得子查询的结果,然后再连接orders和orderstatus表”的顺ิ序工作:andosordid,ไosstatusdate=色lectordid,maxstatusdaທtefro摸rderstatusg肉pbຘyordid这次,等号左端的字段来自相同的表,从而不必连接orders和orderstaທtus这两ä个ฐ表了。尽管好的优化器可能会帮我们做到这一点,但保险起见,一开始就指定这两ä个字段来自相同的表是更明智的选择。为ฦ优化器保留最大的自由á度总是上策。前面已经看到了,非关联子查询可以变成内嵌视图,且改动不大。下面,我们写出“列ต出待办订单”的整个ฐ查询语句:色le9๗ame,oordid,osstatus,ไosstatusdaທtefromcustomersc,orderso,ไorderstaທtusos,色lectordid,maxstaທtusdatelaststatusdatefro摸rderstaທtusg肉pbຘyordidx9hereoordid=osordidaທndnotexists色le9ullfro摸rderstatusos29aທplete'andos2๐ordid=๡oordidandosstatusdate=xlaststatusdateandosordid=xordidandocustid=๡ustid但还有问题,如果最终状态确实是“plete”,我们就没有必要用子查询检查其最新状态了。内嵌视图能帮我们找出最后状态,无຀论它是不是“plete”。所以我们把查询改为“检查已๐知的最新状态”,这个过滤条件非常令人满意:色le9ame,oordid,ไosstatus,osstatusdatefromcustomersc,ไorderso,orderstaທtusos,ไ色lectordid,maxstatusdaທtelaທststatusdatefro摸rderstatusg肉pbyordidx9hereoordid=๡osordidandosstatusdate=xlaststatusdaທteaທndosordid=xordidandosstaທtus!='ูplete'

----ๅ------ๅ---ๅ-ๅ--ๅ--ๅ-ๅ---ๅ-page48-ๅ-ๅ----ๅ-----ๅ--ๅ-ๅ------ๅ-ๅ--ๅ

andocustid=ustid如果进一步利用olap或sql引擎的分析功能,还可以避免对orderstatus的重复参照。不过就此打住,来思考一下我们是如何修改查询的,更重要的是“执行路径exe9path”为何。基本上,正常路径是先扫描orders表,接着利ำ用orderstaທtus表上预计非常高效的索引进行访问。在最后一版的代码中,我们改用完整扫描orderstaທtus的方法,这是为了执行g肉pby。orderstatus中的记录条数一定会比orders中的大好几倍,然而,只以要扫描的数据量来看,估计前者比较小而且可能小很多,这取决于为每张订单保存了多少信息。无法确定哪种方法一定更好,这一切都取决于实际数据。补充说明一点,最好别ี在预期会增大的表上做全表扫描操作若能ม把搜索限制在最近一个月或几个月的数据上则会好些。不过,最后一版的代码肯定比第一版的在9here子句用子查询要好。在结束“大数据量查询”的话题之前๩,有个特殊情况值得一提。当查询要返回非常大量的数据时,该查询很可能不是某个ฐ用户坐在电脑前敲入的命令,而是来自于某个ฐ批处理操作。即便“预备阶段”稍长,只要整个ฐ处理能达到เ令人满意的结果,就是可以接受的。当然,不要忘了,无຀论是不是预备阶段,都会需要资源——cນpu、内存,可能还有临ภ时磁盘空间。即使最基本的查询完全相同,优化器在返回大量数据时所选择的路径,仍可能ม会与返回少量数据时完全不同,了解这一点是有用的。总结:尽早过滤掉不需要的数据。取出数据在表中ณ的比例theproportionsofretrieveddata有个典型的说法:当查询返回的记录数过表中ณ数据总量的10%时,就不要使用索引。这种说法暗示ิ,当常规索引的键指向表中不足1้0่%的记录时,它是高效的。正如第3章中所指出的,这个经验法则建立于许多公司仍对关系数据库有所怀疑的年代,那时,关系数据库一般用于部门级数据库,包含十万行数据的表就被认为是大型表。与含有五亿行数据的表相比,十万行的10%ื不值一提。所以,执行计划ฐ“佳者恒佳”仅是个美好的愿望罢了。就算不考虑“1้0%ื的记录”这条“经验法则ruleofthumbຘ”产生的年代现在的表大小早已๐今非昔比了,要知道,返回的记录数除了与期望响应时间有关之ใ外,它本身并无意义。例如,计算十亿行数据的某字段的平均值,虽然返回结果只有一行,但dbms要做大量工作。甚至没有任何聚合处理,dbms要访问的数据页ษ的数量也๣会造成影响。因为要访问的数据页并非只依赖索ิ引:第3章曾指出,表中记录的物理顺序与索引顺序是否一致,对要访问的页数有极大影响;第5๓章将讨论的一些物理实现也会造成影响,由于数据的物理存储方แ式不同,检索出相同数量的记

---ๅ----ๅ--ๅ-ๅ---------ๅ----page4๒9--ๅ------ๅ--ๅ-ๅ------ๅ-ๅ--ๅ---ๅ

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

------ๅ--ๅ-ๅ---------ๅ-----page5๓0-ๅ-ๅ-----ๅ--ๅ-ๅ------ๅ---ๅ---ๅ-ๅ

可。然而,当很多表连接在一起,并且查询条件要参照ั不同的表时例如taທ和tbຘ,会面临连接顺序的问题。连接顺序的选择,取决于如何更快地过滤不想要的记录。如果统计数据足够精确地反映了表的内容,优化器有可能对连接顺序做出适当选择。当查询仅返回少量记录,且过滤条件直接针对源表时,我们必须保证这些过滤条件高效;对于非常重要的条件,必须事先为ฦ相应字段加上索ิ引,以便查询时使用。索引可用性indexusa逼lity如第3章所述,对某字段使用函数时,则该字段上的索引并不能起作用。当然,你可以建立函数索引fun9dex,这意味着要对函数的结果加索ิ引,而不是为字段加索引。注意,“函数调用”不光是指“显式函数调用”。如果你将某类型的字段与一个不同类型的字段或常量进行比较,则ทdbms会执行“隐式类型转换”隐式调用一个转换函数,如你所料é,这会对性能造成影响。一旦确定重要的搜索条件上有索引,而查询编写方แ式也的确能因索引而提高性能,我们还须进一步区别如下两种情况:使用唯一性索引uniqueindex检索单条记录非唯一性索引non-ๅuniqueindex或基于唯一性索引的范围扫描ranges9查询的效率与索引的使用queryeffi9dexusaທge需要连接join表时,唯一性索引非常有用。然而,当程序获得的原始输入primitiveinput不是查询语句需要的主ว键值时,必须ี通过编程来解决转换问题。这里的“原始输入”指程序接受的数据,可能由使用者输入,也可能从文件中ณ读入。如果查询语句需要的主键值本身,就是根据原始输入利ำ用另一个查询所获得的结果,则说明设计不合理。因为ฦ这意味着一个查询的输出被用作另一个查询的输入,应该考虑合并这两个查询。总结:优秀的查询未必来自优秀的程序。数据散布daທtadispersion当条件是“非唯一性”的,或者条件以唯一性索引上的范围来表达时,dbms就必须ี执行范围扫描。例如:9๗here9d或:9๗heresupplier_naທmelike'somename%'

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

键对应的记录很可能散布在整个ฐ表中ณ,而基于成本的优化器知道这一点。所以,索引范围扫描会使dbms核心逐一读取表的存储页ษ,此时,优化器会决定dbms核心忽略索引对表进行扫描。如第5章所述,许多数据库系统了诸如分区paທrtition和聚集索引9dex等功能ม,直接将可能一并读取的数据存储在一起。其实,数据插入处理也常造成数据丛聚9g保存的现象:如果每条记录插入表时都要加时间戳timestaທmp,则相继插入的记录会彼此紧邻除非我们采取特殊手段避免资源竞争,见第9章的讨论。这其实没有必要,而且关系理论中ณ也没有“顺序”的概念,但在实际中却很可能ม生。因此,当我们在时间戳字段的索ิ引上执行范围扫描、查询时间上接近的索ิ引项时,这些记录可能彼此紧邻๑——如果特意为ฦ此设置了存储选项参数,就更是如此了。现在做一个ฐ假定:键值与特定插入环境无຀关、与存储设置无关,与键值或键值范围对应的记录可能存储在磁盘的任何位置。索引仅以特定顺ิ序来存储键值,而对应的记录随机散落在表中ณ。此时,若既不分区、也不采用聚集索引,则ท需访问的存储区会更多。于是,可能ม出现下列情况:同一个表上有两ä个ฐ可选择性完全相同的索引,但一个索引性能好、一个索引性能差。这种情况在第3章已提到过,下面来分析一下。为ฦ了说明上述情况,先创建一个具有100่000่0条记录的表,这个ฐ表有cນ1、c2和c3三个ฐ字段,c1保存序号1้到1้00่0่000่,c2保存从1到เ2๐0000่00不等的随机数,c3保存可重复、且经常重复的随机值。表面看来,c1和c2都具唯一性,因此具有完全相同的可选择性。索引建在c1上,则表中ณ字段的顺序,与索引中的顺序相符——当然,实际上,对表的删除操作会留下“空洞”,随后又有新า的插入记录填入,所以记录顺ิ序会被打乱。相比之下,索ิ引建在c2上,则表中记录顺序与索引中ณ的顺序无关。下面读取c3๑,使用如下范围条件:9here9some_ຕvalueaທndsome_vaທlue+10如图6-1所示,使用c1้索引有序索引,索引中ณ键的顺序与表中记录顺序相同和c2索ิ引随机索ิ引的性能差ๆ异很大。别忘了造成这种差异的原因:为ฦ了读取c3๑的值,除了访问索引,还要访问表。如果我们有两个复合索引,分别在c1,cນ3和c2,c3๑上,就不会有上述差异了,因为这时不必访问表,从索引中即可获得要返回的内容。图6-1说明的这种性能差ๆ异,也解释了下述情况的原因:有时性能会随时间而降低,尤其是在新系统刚ธ投入生产环境并导入旧系统的大量数据时。最初加载的数据的物理排序,可能是有利于特定查询的;但随后几个月的各种活动破坏了这种顺序,于是性能“神秘”降低30%~40%ื。

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

图6-ๅ1:“索引项顺序与表中记录顺ิ序是否一致”对性能的影响现在很清楚了,“dbຘa可以随时重新า组织数据库”其实是错误的。数据库的重新组织曾一度流行;但不断增加的数据量及9๗9999๗9%正常运行等要求,使得重新า组织数据库变得不再适合。如果物理存储方式很重要,则应考虑第5章讨论过的“自组织结构色lf-ๅanizingstrucນture”之ใ一,例如聚集索引9๗dexe或索引组织表index-anizedtable。但要记住,对某种类型的查询有利,可能对另一种类型的查询不利,鱼与熊掌不可得兼。总结:类似的索引,性能却不同,这可能是物理数据的散布引起的。“”条件的““可索引性””9dexa逼lity对“小结果集,直接条件”的情况而言,适当的索引非常重要。但是,其中ณ也有不适合加索引的例外情况:以下案例,用来判断会计账目是否存在“金额不平”的情况,虽然可选择性很高,但不适合加索ิ引。此例中,有个表glreport,该表包含一个应为0่的字段aທ摸unt_diff。此查询的目的是要追踪会计错误,并找出a摸unt_diff不是0的记录。既然使用了现代的dbms,直接把账目对应成表,并应用从前“纸笔记账”的逻辑,实在有点问题;但很不幸,我们经常遇到这种有问题的数据库。无论设计的质量如何,像a摸unt_diff这样的字段通常不应加索ิ引,因为ฦ在理想情况下每条记录的aທ摸unt_ຕdiff字段都是0。此外,a摸unt_diff字段明显是“非规范化”设计的结果,大量计算要操作该字段。维护一个ฐ计算字段上的索引,代价要高于静态字段上的索ิ引,因为被修改的键会在索ิ引内“移动”,于是索引要承受的开销比简单节点增/删要高。总结:并非所有明确的条件都适合加索引。特别是,频๗繁更新的字段会增加索引维护的成本。回到例子。开者有天来找我,说他已最佳化了以下oraທcle查询,并询问过专家建议:色le9um,totaທlaທounting_period,ไtotalledger,ไtotalt,ไerrorerr_ຕt,ไ9๗tfrom-ๅ-firstin-linevie9๗色le9g_period,ledger,

-------ๅ-----ๅ--ๅ-ๅ--------page53-----ๅ--ๅ------ๅ------ๅ--ๅ-ๅ-

9ttfromglreportg肉pbydeptnum,ledger,aounting_ຕperiodtotal,--色9๗evie9๗色le9๗g_period,ไledger,9๗terr_tfromglreport9hereaທ摸unt_diff0่g肉pbydeptnum,ledger,aທounting_perioderror,-ๅ-thirdin-ๅlinevie9色le9๗g_period,ไledger,9tbad_aທt_9tfromglreport9hereaທ摸unt_diff0g肉pbydeptnum,ledger,ไaounting_ຕperiod9um=errordeptnum+ใaທndtotalaounting_period=erroraທounting_period+andtotaທlledger=๡errorledger+aທnd

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

totaldeptnum=9dtotaທlaounting_period=9g_ຕperiod+aທndtotaທlledger=cນpt_ຕerrorledger+orderbytotaldeptnum,totaທlaounting_period,totaທlledger外层查询9๗here子句中的“+”是oraທcle特有的语法,代表外连接outerjoin。换言之:色lect9๗haທteverfromta,ไtbຘ9๗heretaid=๡t逼d+相当于:色lecນt9haທteverfromtaouterjointbont逼d=๡taid下列sqlplus输出显示了该查询的执行计划:10:16:5๓7sql色tautotra9๗ly10่:17:๘02๐sql37๕ro9๗s色lectedelap色d:00:๘30:0่00่6๔exe9--ๅ---------ๅ---ๅ---ๅ------ๅ--ๅ-ๅ---ๅ--ๅ-ๅ------ๅ---ๅ--ๅ-ๅ---ๅ----ๅ--ๅ-----0่色le9toptimizer=๡choo色cost=177๕95๓54๒cນard=154bytes=161701้0mergejoinoutercost=๡177๕95๓5๓4cນaທrd=154๒bytes=๡16170่21mergejoinoutercost=118๖5๓645๓card=1้54๒bຘytes=1้0่78๖032๐vie9cນost=591้736๔card=1้54bytes=๡5๓39043sortg肉pbycນost=๡59๗17๕3๑6caທrd=154bytes=3๑38854๒taທbຘleaessfullof'ูglreport'cost=๡582๐346cນard=4๒37๕0่894bytes=๡961596๔686๔2sortjoincost=593๑9๗10card=15๓4bຘytes=53๑9๗07๕6vie9๗cນost=59390่8caທrd=๡154bytes=๡53๑9๗087sortg肉pbycost=5๓939๗08card=1้5๓4bytes=400498taທbleaທessfullof'glreport'cost=58๖45๓1้9caທrd=4๒37๕0885๓bytes=11้364301้010่1sortjoincost=5939๗10card=15๓4bytes=5๓3๑901110vie9cນost=59๗3908๖caທrd=1้54bytes=539๗0่

--ๅ-ๅ-ๅ------ๅ---ๅ-----ๅ----ๅ-paທge55--ๅ---------ๅ---ๅ---ๅ------ๅ

12๐1้1้sortg肉pbycost=๡5๓939๗08caທrd=154bytes=56๔98๖1้312tabຘleaessfullof'ูglreport'cost=๡584519card=4๒37๕0885๓bytes=161้722๐745๓staທtistics--ๅ-----ๅ-ๅ-----ๅ----ๅ--ๅ-ๅ---------ๅ--------ๅ-ๅ---ๅ------ๅ--ๅ-ๅ------ๅ-ๅ-1้93recursivecalls0dbblo9sistentgets3794172physicaທlreaທds16๔2๐0redosize22๐1้9bytes色ntviaທsqlto9๗t67๕7bytesre9๗t4sql肉ndtripstofrom9t17๕sortsme摸ry0sortsdi3๑7ro9sprocນes色d在此说明,我没有浪费太多时间在执行计划上,因为ฦ查询本身的文字描述已显示ิ了查询的最大特点:只有四~五百万条记录的glreport表,被访问了三次;每个ฐ子查询存取一次,而且每次都是完全扫描。编写复杂查询时,嵌套查询通常很有用,尤其是你计划将查询划分为多个ฐ步骤,每个步骤对应一个ฐ子查询。但是,嵌套查询不是银弹,上述例子就属于“滥用嵌套查询”。查询中ณ的第一个内嵌视图,计算每个部ຖ门的账目数、会计期、分类账,这不可避免地要进行全表扫描。面对现实吧!我们必须完整扫描glreport表,因为检查有多少个ฐ账目涉แ及所有记录。但是,有必要扫描第二次甚至第三次吗?总结:如果必须进行全表扫描,表上的索ิ引就没用了。不要单从“分析aທnalyticນ”的观点看待处理,还要退一步,从整体角度考虑。除了在aທ摸unt_diff值上的条件之外,第二个内嵌视图所做的计算,与第一个视图完全相同。我们没有必要使用,ไv9,x函数,或使用标准语法99๗el色xend,即可轻松实现这项计算。第三个内嵌视图所过滤的记录与第一个视图相同,但要计算不同账目数。把这个ฐ计数合并到第一个ฐ子查询中并不难:用9๗t_diff为0่时的“账户编号aountnumbຘer”,就很容易统计有多少个不同的账户编号了,当然,记住减1้去掉cນhr1้这个虚拟的账户编号。其中,账户编号字段的类型为varcນhar2注1,而cນhr1้在oracle中代表ascii码值为1的字符——在使用oracle这类用c语言编写的系统时,我总是不敢安心使用chr0่,因为c语言以chr0作为ฦ字符串终止符。sothisisthesuggestionthatireturnedtothedeveloper:色le9๗g_period,ไledger,

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

9๗b,ไsumde9๗t_diff,0,ไ0,1err_t,ไ99๗t_diff,ไ0,9t-1bad_at_9๗tfromglreportg肉pbydeptnum,ledger,aounting_ຕperiod这个新的查询,执行度是原先的四倍。这丝毫不令人意外,因为ฦ三次的完整扫描变成了一次。注意,查询中不再有9๗here子句:a摸unt_ຕdiff上的条件已๐被“迁移”到เ了色lect列表中decນode函数执行的逻辑,以及由g肉pbຘy子句执行的聚合aທggregation中。使用聚合代替过滤条件有点特殊,这正是我们要说明的“九种典型情况”中ณ的另一种——以聚合函数为基础获得结果集。总结:内嵌查询可以简化查询,但若使用不慎,可能造成重复处理。小结果集,间接条件smallresult色t,indirectcriteria与上一节类似,这一节也是要获取小结果集,只是查询条件不再针ฤ对源表,而是针对其他表。我们想要的数据来自一个表,但查询条件是针对其他表的,且不需要从这些表返回任何数据。典型的例子是在第4章讨论过的“哪些客户订购了特定商品”问题。如第4๒章所述,这类查询可用两种方法表达:使用连接,加上distinct去除结果中的重复记录,因为有的客户会多次订购相同商品使用关联或非关联子查询如果可以使用作用于源表的条件,请参考前一节“小结果集,直接条件”中ณ的方แ法。但如果找不到这样的条件,就必须多加小心了。取用第4章中例子的简化版本,找出订购蝙蝠车的客户,典型实现如下:色le9ctorders9orderdetailonorderdetaທilordid=ordersordidjoinaທrti9articlesaທrtid=๡orderdetailartid9hereaທrti9ame='bat摸逼le'依我看,明确使用子查询来检查客户订单是否包含某项商品,才是较好的方式,而且也比较容易理解。但应该采用“关联子查询”还是“非关联子查询”呢?由于我们没有其他条件,所以答案

--ๅ-------ๅ--ๅ----ๅ--ๅ--ๅ-ๅ---page57๕----------ๅ--ๅ--ๅ----ๅ--ๅ-ๅ--

应该很清楚:非关联子查询。否则,就必须扫描orders表,并针ฤ对每条记录执行子查询——当orders表规模小时通常不会查觉其中问题๤,但随着orders表越来越大,它的性能ม就逐渐让我们如坐针毡了。非关联子查询可以用如下的经典风格编写:色le9ctorderscນustidfro摸rders9hereordidin色lectorderdetailsordidfro摸rderdetailjoinarti9๗articlesaທrtid=orderdetaທilartid9hereaທrti9aທme='ูbaທt摸逼le'或采用from子句中ณ的子查询:色le9๗ctorderscustidfro摸rders,色lectorderdetailsordidfro摸rderdetailjoinarti9๗articlesartid=๡orderdetailaທrtid9hereaທrti9๗ame=๡'ูbat摸逼le'aທssub_q9๗heresubຘ_ຕqordid=ordersordid我认为ฦ第一个ฐ查询较为易读,当然这取决于个人喜好。别忘了,在子查询结果上的in条件暗含了distinct处理,会引起排序,而排序把我们带到了关系模型的边缘。总结:如果要使用子查询,在选择关联子查询、还是非关联子查询的问题上,应仔细考虑。多个ฐ宽泛条件的交集smaທllinter色9ofoadcນriteria本节讨论对多个宽泛条件取交集获得较小结果集的情况。在分别使用各个条件时,会产生大型数据集,但最终各个大型数据集的交集却是小结果集。继续上一节的例子。如果“判断订购的商品是否存在”可选择性较差,就必须考虑其他条件否则ท结果集就不是小结果集。在这种情况下,使用正规连接、关联子查询,还是非关联子查询,要根据不同条件的过滤能ม力和已๐存在哪些索引而定。例如,由于不太畅销,我们不再检索订购蝙蝠车的人,而是查找上周六购买某种肥皂的客户。此时,我们的查询语句为ฦ:色le9๗ctorders9๗orderdetail

-----ๅ--ๅ-ๅ---ๅ------ๅ---ๅ--ๅ-ๅpaທge58๖--------ๅ-ๅ-----ๅ----ๅ--ๅ-ๅ--

onorderdetaທilordid=ordersordidjoinaທrti9articlesartid=๡orderdetailaທrtid9herearti9d这个处理流程很合逻辑,该逻辑和商品具有高可选择性时相反:先取得商品,再取得包含商品的明细订单,最后处理订单。对目前๩讨论的肥皂订单的情况而言,我们应该先取得在较短期间内下的少量订单,再检查哪些订单涉แ及肥皂。从实践角度来看,我们将使用完全不同的索引:第一个ฐ例子需要orderdetail表的商品名称、商品id这两个字段上的索引,以及orders表的主键orderid上的索引;而此肥皂订单的例子需要orders表日期字段的索ิ引、orderdetail表的订单id字段的索引,以及articles表的主键orderid上的索ิ引。当然,我们先假设索引对上述两例都是最佳方式。要知道哪些客户在上星期六买了肥皂,最明显而自然的选择是使用关联子查询:色le9cນtorderscustidfro摸rders9hereandexists色lecນt1้fro摸rderdetailjoinaທrti9articນlesartid=orderdetailartid9herearti9dorderdetailsordid=ordersordid在这个方แ法中,为了使关联子查询度较快,需要orderdetail表的ordid字段上有索引就可以通过主键artid取得商品,无຀需其他索ิ引。第3章已提到เ,事务处理型数据库traທnsa9aldaທtaທba色的索ิ引是种奢侈,因为它处在经常更改的环境中,维护的成本很高。于是选择“次佳”解决方案:当表orderdetail上的索ิ引并不重要,而且也有充足理由不再另建索ิ引时,我们考虑以下方式:色le9cນtorderscustidfro摸rders,色lectorderdetailsordidfro摸rderdetail,aທrticles9herearticນlesartid=orderdetaທilartidandarti9aທme='soap'ูassub_q9heresub_qordid=๡ordersordidand

----------ๅ--ๅ-ๅ-----ๅ--ๅ-ๅ--page59-ๅ---ๅ-ๅ--ๅ--ๅ-ๅ------ๅ-----ๅ-ๅ-

这第二个方法对索ิ引的要求有所不同:如果商品数量不过数百万项ำ,即使aທrtname字段上没有索引,基于商品名称条件的查询性能也不错。表orderdetaທil的artid字段可能ม也๣不需索引:如果商品很畅ม销,出现在许多订单中ณ,则表orderdetaທil和articນles之间的连接通过哈希或合并连接mergejoin更高效,而artid字段上的索引会引起嵌套的循环。与第一种方法相比,第二种方แ法属于索引较少的解决方案。一方แ面,我们无法承受为ฦ表的每个字段建立索ิ引;另一方面,应用中都有一些“次要的”查询,它们不太重要,对响应时间要求也不苛刻,索引较少的解决方案完全满足它们的要求。总结:为现存的查询增加搜索条件,可能彻底改变先前๩的构想:修改过的查询成了新查询。多个ฐ间接宽泛条件的交集smaທllinter色9๗directoadcriteria为ฦ了构造查询条件,需要连接join源表之外的表,并在条件中使用该表的字段,就叫间接条件indire9。正如上一节“多个宽泛条件的交集”的情况,通过两个或多个宽泛条件的交集处理获取小结果集,是项艰难的工作;若是涉แ及多次join操作,或者对中心表9操作,则ท会更加困难——这是典型的“星形schemaທstarscນhemaທ”第10่章详细讨论,实际的数据库系统中经常遇到。对于多个可选择性差的条件,一些罕见的组合要求我们预测哪些地方แ会执行完整扫描。当牵涉到เ多个ฐ表时,这种情况颇值得研究。dbms引擎的执行始于一个ฐ表、一个索引或一个分区,就算dbຘms引擎能并行处理数据也是如此。虽然由多个大型数据集合的交集所定义的结果集非常小,但前期的全表扫描、两次扫描等问题依然存在,还可能在结果上执行嵌套循环nestedloop、哈希连接hashjoin或合并连接mergejoin。此时,困难在于确定结果集的哪种表组合产生的记录数最少。这就好比,找到防线最弱的环节,然后利用它获得最终结果。下面通过一个实际的oracle案例说明这种情况。原始查询相当复杂,有两个表在from子句中都出现了两ä次,虽然表本身不太庞大大的包含70่0่000行数据,但传递给查询的九个ฐ参数可选择性都太差:色lectdaທtafromttex_ຕaທ,ttex_ຕb,ttraomaທ,topeoma,ไttypobj,ttrcນap_a,ไttrcນap_b,trgppdt,tstg_afromttrcappttrcap_a,ไttrcappttrcaທp_ຕbຘ,tstgtstg_a,

-ๅ---ๅ-ๅ--ๅ--ๅ-ๅ---------ๅ--ๅ-ๅ-page60--ๅ--ๅ-ๅ---ๅ----ๅ--ๅ----ๅ--ๅ--ๅ-ๅ

topeomaທ,ttraoma,ttexttex_a,ttexttex_bຘ,tbຘooks,ไtpdt,trgppdt,ไttypobj9aທpeomatxnumandttraທomaທbຘk9dttex_bຘtrs9dttraomaທtrs9um9tt9um9dttypobjobjtyp=ttraທo毛bjtypandttraomatrs9dttr9๗ot色le9dttr9ot色le9๗dttraomapdt9dtpdtrityp=๡trgppdtritypandtpdtriflg=trgppdtriflgandtpdtpdt9๗dtrgppdtrityp=:2--ๅnot色le9๗dtrgppdtriflg=:3-ๅ-ๅnot色le9um=tstg_atxnumandttr9dtstg_arityp=:๘4--ๅnot色le99๗ot色le9um=:๘8--not色lecນtive我们适当的参数这里以:0่到เ:8代表执行此查询:耗时过25秒,返回记录不到20่条,做了3๑000次物理io,访问数据块30่00000่次。上述统计数据反映了实际执行的情况,这是必须ี先明确的。下面,通过查询数据字典,得到表记录数情况:taທble_naທmenum_ro9๗s-ๅ-ๅ------ๅ---ๅ---ๅ-----ๅ----ๅ--ๅ-ๅ---------ๅ--ttypobj186๔trgppdt3๑6๔6tpdt5๓370topeoma12๐1้18๖ttraoma12118๖tbຘooks1้2268

-----ๅ--ๅ-ๅ------ๅ-----ๅ-ๅ--ๅ-ๅpaທge61---ๅ-------ๅ--ๅ--ๅ----ๅ--ๅ-ๅ--

ttex1้02554ttrcaທpp187759tstg70่24๒0่3认真研究表及表的关联情况,得到图6-2๐所示的分析图:小箭头代表较弱的选择条件,方แ块为表,方块的大小代表记录数多少。注意:在中ณ心位置的ttraoma表,几乎ๆ和其他所有表有关联关系,但很不幸,选择条件都不在ttraoma表。另一个有趣的事实是:上述的查询语句中,我们必须trgppdt表的rityp字段和riflg字段的值作为条件——为ฦ了连接jointrgppdt表和tpdt表要使用这两个字段和pdtcod字段。在这种情况下,应该思考倒转此流程——例如把tpdt表的字段与所的常数做比较,然后只从trgppdt表取得数据。

--ๅ-ๅ----ๅ---ๅ---ๅ---ๅ------ๅ-paທge62-----ๅ------ๅ---ๅ-ๅ--ๅ--ๅ-ๅ---ๅ

图6-2:数据的位置关系多数dbຘms“检查优化器选择的执行计划”这一功能ม,比如通过explaທin命令直接检查内存中执行的项ำ目。上述查询花了2๐5秒虽然不是特别糟,通常是先完整扫描ttraoma表,接着进行一连串的嵌套循环,使用了各种高效的索引详述这些索引很乏味,我们假设所有字段都建立了合适的索引。度慢的原因是完整扫描吗?当然不是。为ฦ了证明完整扫描所花时间占的比例甚微,只需做如下简单的测试:读取ttraomaທ表的所有记录;为了避免受到เ字符显示ิ时间的干扰,这些记录无຀需显示。优化器现:tstg表有“大量敌军”,而查询中针ฤ对此表的选择条件比较弱,所以难以对它形成“正

---ๅ---ๅ--ๅ-ๅ--ๅ-ๅ---------ๅ--ๅpage63๑----ๅ--ๅ-ๅ---ๅ----ๅ--ๅ-----ๅ-ๅ-

面攻击”;而ttrcapp表在查询的from子句中出现两次,但基于该表的判断条件也较弱,所以也不会带来查询效率的提升;但是,ttraທomaທ表的位置显然很关键,且该表比较小,适合作为“第一攻击点”——优化器会毫不犹豫地这么做。那么,既然对ttraoma表的完整扫描无可厚非,优化器到底错在哪里呢?请看图6-3所示的查询执行情况。

------ๅ------ๅ--ๅ-ๅ---ๅ-----page64-----ๅ--ๅ--ๅ-ๅ------ๅ-------

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

-ๅ--ๅ-ๅ------ๅ---ๅ---ๅ---ๅ----paທge6๔5--------ๅ------ๅ---ๅ-ๅ--ๅ--ๅ-ๅ

注意观察图中所示的操作执行顺ิ序,查询度慢的原因显露无遗:我们的查询条件很糟糕,优化器选择完全忽略它们。优化器决定先对ttraoma表进行完整扫描;接着,访问和表ttraທomaທ关联的所有小型表;最后,对其他表运用我们的过滤条件。这样执行是错误的:虽然优化器决定先访问表ttraoma有道理该表的索ิ引可能非常高效,每个键平均对应的记录数较少,或者索ิ引与记录的顺序有较好的对应关系,但将我们的查询条件推迟执行,不利于减少要处理的数据量。既然已访问了ttraoma这个关键表,应该紧接着执行语句中的查询条件,这样可以借助这些表与ttraoma表之间的连接join先去除ttraoma表中ณ无用的记录——甚至在结果集更大时,如此执行的效率仍比较高。但是上述信息我们知道,“优化器”却无຀从知道。怎样才能迫使dbms依我们所要求的方式执行查询呢?要依靠sql方言sqldiaທlecນt。正如你将在第1้1章看到的,多数sql方言都支持针对优化器的指示ิ或提示hint,虽然各种方言所用语法不同;例如,告诉优化器按表名在from子句中ณ出现的顺ิ序依次访问各表。不过,“提示”的实际影响远比它的名字暗示ิ的要大得多,采用“提示”的问题๤在于,每个ฐ提示都是在“赌未来”——我们已强制规定了执行路径,所以环境、数据量、数据库算法、硬件等因素的展变化即使不能绝对适合我们的执行路径,也应该基本适合。例如,既然索ิ引的嵌套循环是最高效选择,并且嵌套循环不会因并行化而受益,那么命令优化器按照表的排列ต顺序访问它们几乎没什么เ风险。明确指定表的访问顺ิ序,就是这个案例中实际采用的方法,最终查询不到1秒即可完成,不过物理io次数减少并不明显原来3000次,现在2340่次,因为我们仍以ttraທoma表的完整扫描开始,但逻辑io次数的大幅降低从30่0่00่00次降到เ1้65๓0่0次使总体响应时间显着缩短,因为我们“建议”了更高效的执行路径。总结:记住,你应该详细说明所有强迫dbຘms做的事。显式地通过优化器指令,指定表的访问顺ิ序,是个笨拙的方法。更优雅的方แ法是在from子句中采用嵌套查询,在数值表达式中建议连接关系,这样不必大幅修改sql子句:色lect色lecນtlistfrom色le9um,ttraທomaທbຘkcod,ttraທomaທtrscod,ttraທomapdtcod,ไttrao毛bjtyp,ไfromttraoma,tstgtstg_ຕa,ttrcນappttr9cod=:๘7

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

andtstg_ຕaທstgnum=:8andtstg_ຕaທrityp=:4andttraທomatxnum=๡tstg_aທtxnumaທndttr9๗dttrcaທp_arefcod=:๘9appttrcap_b,tbooks,topeoma,ไttexttex_ຕb,ttypobj,tpdt,ไtrgppdt9aທpeomatxnumandabຘk9๗dttex_btrs9dttex_antt9๗um9๗dttypobjobjtyp=aທobjtypaທndatrs9๗dttr9daທpdt9๗dtpdtrityp=trgppdtritypaທndtpdtriflg=trgppdtriflgaທndtpdtpdt9dtpdtrityp=๡:2๐aທndtpdtriflg=:3๑aທndttrcap_ຕefcນod=๡:6通常,没有必要采用非常具体的方式和难以理解的提示,其实,正确的最初指导就可使优化器找到正确的执行路径。嵌套查询是个不错的选择,它使表的关联变得明确,而sql语句的阅读也相当容易。总结:混乱的查询会让优化器困惑。结构清晰的查询及合理的连接建议,通常足以帮助优化器提升性能。大结果集largeresult色t无຀论结果集是如何获得的,只要结果集“很大”,就符合我们下面要讨论的“大结果集”的情况。

-ๅ-ๅ------ๅ---ๅ-----ๅ----ๅ--ๅ-ๅpage67๕---------ๅ---ๅ---ๅ------ๅ--ๅ

批处理环境下,产生大结果集是明智的。当需要返回大量记录时,只要查询条件的可选择性不高,那么即使结果集只占表中ณ数据量的一小部分,也๣会引起dbms引擎执行全表扫描;只有某些数据仓库例外,我们将在第10章中讨论之。如果查询返回几万条记录,那么使用索引是没有意义的,无຀论索引用于产生最终结果,还是用于复杂查询的中间步骤。相比而言,借助哈希或合并连接进行全表扫描是合适的。当然,强力手段背后也๣必须有智慧:我们必须尽量扫描数据返回比例最高的表、索引,或者这两者的分区;扫描时的过滤条件必须是粗粒度的,从而返回的数据量比较大,使扫描更有价值;扫描显然违背了“尽快去除不必要数据”这一原则,但一旦扫描结束应立即重新า贯彻该原则。相反,采取扫描方式不合适的情况下,应尽量减少要访问数据的块数。为此,最常用的手段就是使用索引而不是表,尽管所有索ิ引的总数据量经常比表还大,但单个ฐ索引则远比表要小。如果索ิ引包含了所有需要的信息,则扫描索引而不扫描表是相当合理的,可以利用诸如聚集索引等避免访问表的技术。无论是要返回大量记录,还是要对大量记录进行检查,每条记录的处理都需小心。例如,一个性能不佳的用户自定义函数的调用,如果生在“返回小结果集的色lect列表”中或在“可选择性很高的9๗here子句”中,则影响不大;但返回大数据集的查询可能ม会调用这个函数几十万次,dbms服务器就不堪重负了,这时必须ี优化代码。还要重点关注子查询的使用。处理大量记录时,关联子查询correlatedsubຘquery是性能杀手。当一个查询包含多个子查询时,必须让它们操作各不相同、自给自足的数据子集,以避免子查询相互依赖;到查询执行的最后阶段,多个子查询分别ี得到的不同数据集经过哈希๶连接或集合操作得到เ结果集。查询执行的并行化paທrallelism也๣是个好主意,不过只应在“并活动会话数9tlya9s”很少典型情况为批处理操作时才这么做。并行化是由dbms实现的,如果有可能,dbຘms把一个查询分割为ฦ多个并行运行的子任务,并由另一个专门的任务来协调。并用户数很大时,并行化反而会影响处理能力。一般而言,并用户数又多、要处理的信息量又大的情况下,最好做好战斗ç准备,因为这经常靠投入更多硬件来解决。除了处理过程中ณ由á资源争用引起的等待之外,查询必须访问的数据量是影响“响应时间”的主要因素า。但正如第4章讲过的,最终用户并不关心客观的数据量分析,他们只关心查询获得的数据。基于一个表的自连接色lf-ๅjoinsonoaທble利用卓越的、广为流行的范式注2๐,有助于我们设计正确的关系数据库至少满足3๑nf。所有非键字段均与键相关、并完整依赖于键,非键字段之间没有任何依赖。每条记录具有逻辑一致性,同一个ฐ表中没有重复记录。于是,才能够建立同一个ฐ表之间的连接关系:使用同一查询从同一表中选择不同记录的集合可以相交,然后连接它们,就好像它们来自不同表一样。本节将讨论简单的自连接。本节不讨论较复杂的嵌套层次结构,这一主题在第7章中ณ讨论。自连接,指表与自身的连接,这种情况比分层查询更常见。自连接用于“从不同角度看待相同数据”的情况,例如,查询航班会两次用到เairports表,一次找到“出机场”的名称,另一次找出“到เ达机场”的名称:色le9๗umber,aairport_namedeparture_aທirport,

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

bຘaທirport_ຕnamearrival_airportfromflightsf,ไaທirportsa,ไairportsbຘ9๗herefdep_iata_9dfarr_ຕiata_ຕcode=逼ataທ_ຕcode此时,一般规则ท仍然适用:重点保证索引访问的高效。但是,如果此时索引访问不太高效怎么办呢?当其冲地,应避免“第一轮处理丢弃了第二轮处理所需的记录”。应该通过一次处理收集所有感兴趣的记录,再使用诸如ca色语句等结构分别显示ิ记录,第1้1章将详细说明这种方法。非常微妙的是,有些情况看似与“机场的例子”很像,但其实不然。例如,如何利用一个保存“定期累计值”注3的表,显示每个时间段内累计值的增量?此时,该表内的两ä个不同记录间虽然有关联,但这种关联很弱:两个记录之所以相关,是因为它们的时间戳之间有前后关系。而连接两个flights表是通过airports表进行的,这种关联很强。例如,时间段为ฦ9edaທte”表示,则ท查询如下:色lectatimestamp,ไastaທtisti9ter5hits_per_minutefromhit_ຕ9terbຘ9๗herebtimestaທmp=๡atimestamp+3๑00andbຘstaທtisticນ_id=astatistic_ຕidorderbyatimestamp,astaທtisticນ_id上述脚本有重大缺陷:如果第二个ฐ累计值不是正好在第一个累计值之ใ后5分钟取得的,那ว么就无຀法连接这两条记录。于是,我们改以“范围条件”定义连接。查询如下:色lecນtatimestaທmp,ไastatisti9ter60่btimestaທmp-atimestamphits_per_ຕminutefromhit_ຕ9๗terbຘ9herebtimestampbet9ap+2๐00่aທndatimestamp+400่andbstaທtistic_id=๡astatistic_ຕidorderbyatimestamp,aທstatistic_id这个ฐ方法还是有缺陷:前后两ä次计算累计值的时间间隔,如果不介于200到เ40่0่秒之间例如取样频率改变了,如此之大的时间跨度就会引起风险。我们还有更安全的方法,就是使用基于“记录窗口9indo9sofro9๗apfun9。难以想象,这种本质上不太符合关系理论的技术可以显着提升性能ม,但应作为查询优化的最后手段使用。借助paທrtition子句,olap函数支持“分别ี处理结果集的不同子集”,比如

--ๅ--ๅ----ๅ--ๅ-ๅ---ๅ----ๅ--ๅ---paທge6๔9๗---ๅ------ๅ---ๅ-----ๅ----ๅ--ๅ

分别对它们进行排序、总计等处理。借助olap函数ro9_ຕnumbຘer,可以根据statistic_id建立子集,然后按时间戳增大的顺序为ฦ不同统计赋予连续整数编号,接下来,就可以连接statistic_id和两ä个序号了,如下例子所示:色lectaທtimestamp,astatisti9๗ter60btimestaທmp-atimestaທmpfrom色lecນttimestaທmp,statisti9๗umberoverpartitionbຘystatistic_idorderbytimestamprnfromhit_ຕ9tera,ไ色lecttimestamp,ไstatisti9๗umbຘeroverpartitionbystatistic_ຕidorderbytimestamprnfromhit_9+ใ1andaທstaທtistic_id=bstatistic_idorderbyaທtimestamp,aທstatistic_ຕidoracle等dbms支持olap函数lag9。该函数借助分区和排序,返回9个值。如果使用lag函数,我们的查询甚至执行得更快——比先前的查询大约快29๗ounter6๔0timestaທmp-ๅprev_timestaທmpfrom色lecttimestamp,staທtisti9ter,1overpartitionbystatistic_ຕidorderbຘytimestampprev_9๗ter,laທgtimestamp,1้overpartitionbຘystatistic_idorderbຘytimestampprev_ຕtimestampfromhit_9๗teraທorderbyaທtimestamp,astatistic_id很多时候,我们的数据并不像航班案例中那ว样具有对称性。通常,当需要查找和最小、最大、最早、或最近的值相关联的数据时,先必须找到这些值本身此为ฦ第一遍扫描,需比较记录,

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

接下来的用这些值作为第二遍扫描的搜索条件。而以滑动窗口sliding9๗indo9๗aທp函数,可以将两遍扫描合而为一至少表面上如此。基于时间戳或日຅期的数据查询,非常特殊也๣非常重要,本章在稍后的“基于日期的简单搜索ิ或范围搜索”中专门讨论。总结:当多个ฐ选取条件用于同一个ฐ表的不同记录时,可以使用基于滑动窗口工作的函数。基于一个表的自连接色lf-joinsonoable利用卓越的、广为流行的范式注2,有助于我们设计正确的关系数据库至少满足3nf。所有非键字段均与键相关、并完整依赖于键,非键字段之间没有任何依赖。每条记录具有逻辑一致性,同一个ฐ表中没有重复记录。于是,才能够建立同一个表之间的连接关系:使用同一查询从同一表中选择不同记录的集合可以相交,然后连接它们,就好像它们来自不同表一样。本节将讨论简单的自连接。本节不讨论较复杂的嵌套层次结构,这一主ว题在第7章中讨论。自连接,指表与自身的连接,这种情况比分层查询更常见。自连接用于“从不同角度看待相同数据”的情况,例如,查询航班会两次用到aທirports表,一次找到เ“出机场”的名称,另一次找出“到达机场”的名称:色le9umber,aairport_ຕnaທmedeparture_ຕairport,baທirport_namearrivaທl_aທirportfromflightsf,aທirportsa,aທirportsb9herefdep_iaທta_9๗dfarr_ຕiaທta_code=๡逼aທta_ຕcode此时,一般规则仍然适用:重点保证索ิ引访问的高效。但是,如果此时索引访问不太高效怎么เ办呢?当其冲地,应避免“第一轮处理丢弃了第二轮处理所需的

↑返回顶部↑

书页/目录

SQL语言艺术