返回

SQL语言艺术

关灯
护眼
第1章(1 / 1)
强烈推荐: 豪门首席女秘书 不死村医 天降魔法师 神秘总裁,别玩了 和校花一起查房的日子 美女上司俏房客 美女上司俏房客 仙剑奇侠之剑魂问世 嫡妃的三亩田园

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

sqlssqqll语言艺术内容介绍本书๰分为ฦ12๐章,为ฦ你的实际工作提出了具体,非常便于实践。尽管当今新潮技术层出不ຘ,对九种sql经典查询场景以及其性能影响讨论,并通过举例&#x,每一章包含许多原则或准则。数据处理依然处于我们系统的核心地位,而且需管理的,尤其是数据库应用维护人员阅读。资深sql专家stéphanefa肉lt倾力打造《软件架构设计》作者温昱最新译作巧妙借鉴《孙子兵法》的智慧结晶传授25年的sql性能ม与调校经验深入探讨九种常见查询方แ案及其性能前言过去,“信息技术it”的名字还不如今天这般耀眼,被称为“电å子数据处理”。其实,这些例子大多来自于实际案例,本书๰适合sql数据库开者、软件架,也๣适合dbaທ。今天,最重要的集团数据都被保存在数据库中,sql语言虽有缺点。通过sql语言来&#x,但非常流行,它从1้980年代早期开始被广泛接受,如今。随后就æ,年轻开者在接受面试时,没有谁不宣称自己能熟练应用sql的。sql作为ฦ数据库访问语言,已๐成为任何基础it课程的必备部ຖ分。开者宣传自己้熟练掌握sql,其实前๩提是“熟练掌握”的定义是“能够获得功能上正确的结果”。然而,全世界ศ的企业如今都面临数据量的爆炸式增长,所以仅做到เ“功能正确”是不够的,还必须ี足够快,所以数据库性能成了许多公司头疼的问题๤。有趣的是,尽管每个ฐ人都认可性能问题๤源自代码,但普遍接受的事实则是开者的要关注点应该是功能正确。人们认为:为ฦ了便于维护,代码中的数据库访问部ຖ分应该尽量简单;“拙劣的sql”应该交给资深的dba去摆弄,他们还会调整几个ฐ“有魔力”的数据库参数,于是度就快了——如果数据库还不够快,似乎就该升级硬件了。往往就是这样,那些所谓的“常识”和“可靠方แ法”最终却是极端有害的。先写低效的代码、后由专家调优,这种做法实际上是自找麻烦。本书认为,先要关注性能的就是开者,而且sql问题๤绝不仅仅只包含正确编写几个查询这么เ简单。开者角度看到的性能ม问题和dbaທ从调优角度看到的大相径庭。对dba而言,他尽量从现有的硬件如处理器和存储子系统和特定版本的dbms获得最高性能ม,他可能有些sql技能ม并能调优一个ฐ性能极差的sql语句。但对开者而言,他编写的代码可能要运行5๓到เ10่年,这些代码将经历一代代的硬件,以及dbຘms各种重要版

-------ๅ------ๅ--ๅ----ๅ--ๅ-ๅ-ๅpage2---ๅ---------ๅ-ๅ--ๅ---ๅ-ๅ-ๅ---

本升级例如支持互联网访问、支持网格,不一而足。所以,代码必须ี从一开始就快、健全。很多开者仅仅是“知道”sql而已,他们没有深刻๑理解sql及关系理论,实在令人遗憾ย。为ฦ何写作本书sql书主ว要分为ฦ三种类型:讲授具体sql方言的逻辑和语法的书、讲授高级技术及解决问题方法的书、专家与资深dba所需的性能和调优的书๰。一方แ面,书籍要讲述如何写sql代码;另一方แ面,要讲如何诊断和修改拙劣的sql代码。在本书中,我不再为ฦ新า手从头讲解如何写出优秀的sql代码,而是以越单个ฐsql语句的方式看待sql代码,无຀疑这更加重要。教授语言使用就够难了,那ว么เ本书是怎样讲述如何高效使用sql语言的呢?sql的简单性具有欺骗性,它能支持的情况组合的数目几乎是无限的。最初ม,我觉得sql和国际象棋很相似,后来,我悟到明国际象棋是为了教授战争之ใ道。于是,每当出现sql性能难题的时候,我都自然而然地将之ใ视为ฦ要和一行行数据组成的军队作战。最终,我找到了向开者传授如何有效使用数据库的方แ法,这就像教军官如何指挥战争。知识、技能、天赋缺一不可。天赋不能传授,只能培养。从写就了《孙子兵法》的孙子到如今的将军,绝大多数战略家都相信这一点,于是他们尽量以简单的格言或规则ท的方แ式表达沙场经验,并希望这样能指导真实的战争。我将这种方法用于战争之ใ外的许多领ๆ域,本书借鉴了孙子兵法的方แ法和书๰的题目。许多知名it专家冠以科学家称号,而我认为“艺术”比“科学”更能ม反映it活动所需的才能、经验和创น造力注1้。很可能是由于我偏爱“艺术”的原因,“科学”派并不赞成我的观点,他们声称每个sql问题都可通过严å格分析和参考丰ถ富的经验数据来解决。然而,我不认为这两ä种观点有什么不一致。明确的科学方แ法有助于摆脱单个ฐ具体问题๤的限制,毕竟,sql开必须考虑数据的变化,其中ณ有很大的不确定性。某些表的数据量出乎意料é地增长将会如何?同时,用户数量也๣倍增了,又将会如何?希望数据在线保存好几年将会如何?如此一来,运行在硬件之上的这些程序的行为是否会完全不同?架构级的选择是在赌未来,当然需要明确可靠的理论知识——但这是进一步运用艺术的先决条件。第一次世界ศ大战联军总司令ferdinandfo9๗checolesupérieuredeguerre的一次演讲中说:战争的艺术和其他艺术一样,有它的历史和原则ท——否则ท,就不能成其为艺术。本书不是cookbຘook,不会列出一串问题๤然后给出“处方”。本书的目标重在帮助开者和他们的经理提出犀利的问题。阅读和理解了本书之ใ后,你并不是永不再写出丑陋缓慢的查询了——有时这是必须的——但希๶望你是故意而为之、且有充足的理由á。目标读者本书๰的目标读者是:有丰富经验的sql数据库开者他们的经理数据库占重要地位的系统的软件架构师我希๶望一些dbຘaທ、尤其是数据库应用维护人员也๣能ม喜欢本书。不过,他们不是本书๰的主ว要目标读者。本书假定

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

本书๰假定你已精通sql语言。这里所说的“精通”不是指在你大学里学了sql101并拿来aທ+ใ的成绩,当然也并非指你是国际公认的sql专家,而是指你必须具有使用sql开数据库应用的经验、必须考虑索引、必须不把50่0่0่行的表当大表。本书的目标不是讲解连接、外连接、索引的基础知识,阅读本书๰过程中,如果你觉得某个ฐsql结构还显神๰秘,并影响了整段代码的理解,可先阅读几本其他sql书。另外,我假定读者至少熟悉一种编程语言,ไ并了解计算机程序设计的基本原则。性能ม已๐很差ๆ、用户已๐抱怨、你已在解决性能问题๤的前๩线,这就是本书的假定。本书内容我现sql和战争如此相像,以至于我几乎ๆ沿用了《孙子兵法》的大纲,并保持了大部分题目名称注2๐。本书分为1้2章,每一章包含许多原则ท或准则,并通过举例的方式对原则进行解释说明,这些例子大多来自于实际案例。第1章,制定计划:为ฦ性能ม而设计讨论如何设计高性能数据库第2章,动战争:高效访问数据库解释如何进行程序设计才能高效访问数据库第3๑章,战术部署:建立索引揭示ิ为何建立索ิ引,如何建立索引第4章,机动灵活:思考sql语句解释如何设计sql语句第5๓章,了如指掌:理解物理实现揭示物理实现如何影响性能第6章,锦囊妙计:认识经典sql模式包括经典的sql模式、以及如何处理第7๕章,变换战术:处理层次结构说明如何处理层次数据第8章,孰优孰劣:认识困难,处理困难指出如何认识和处理比较棘手的情况第9章,多条战线:处理并讲解如何处理并第10章,集中兵力:应付大数据量讲解如何应付大数据量第1้1章,精于计谋:挽救响应时间分享一些技巧,以挽救设计糟糕的数据库的性能第12章,明察秋毫:监控性能ม收尾,解释如何定义แ和监控性能本书约定本书使用了如下印刷惯例:等宽courier表示sql及编程语言的关键字,或表示ิtable、索ิ引或字段的名称,抑或表示函数、代码及命令

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

输出。等宽黑体cນourier表示必须由用户逐字键入的命令等文本。此风格仅用于同时包含输入、输出的代码示例。等宽斜体courier表示这些文本,应该被用户的值代替。总结:箴言,概括重要的sql原则。注意提示、建议、一般性注解。为ฦ相关主ว题有用的附加信息。代码示例本书๰是为了帮助你完成工ื作的。总的来说,你可以将本书๰的代码用于你的程序和文档,但是,若要大规模复制代码,则必须ี联系o'reilly申ã请授权。例如:编程当中ณ用了本书的几段代码,无需授权;但出售或分o'reilly书籍中ณ案例的cນd-ๅrom光盘,需要授权。再如:回答问题๤时,引用了本书或其中的代码示例,无需授权;但在你的产品文档中大量使用本书๰代码,需要授权。o'reilly公司感谢但不强制ๆ归属声明。归属声明通常包括书๰名、作者、出版商、isbn。例如“theaທrtofsqlbystéphanefaທ肉lt9ithpeterrobຘsoncopyrightcນ20่06o'ูreillymediaທ,ไ0-ๅ5๓9๗6๔-00่894-5”。如果你对代码示例的使用出了上述范围,请通过permissions@๤oreilly联系出版商。评论与提问我们已尽力核验本书所的信息,尽管如此,仍不能ม保证本书๰完全没有瑕疵,而网络世界ศ的变化之ใ快,也使得本书永不过时的保证成为ฦ不可能。如果读者现本书内容上的错误,不管是赘字、错字、语意不清,甚至是技术错误,我们都竭诚虚心接受读者指教。如果您有任何问题,请按照以下的联系方式与我们联系。o'ูreillymediaທ,in9high9๗aynorth色bຘastopol,9theusor9๗ternationalorlocal7๕07๕8๖2๐9๗-0104๒fax致谢本书๰原版用英语写作,英语既不是我的家乡话,又不是我所在国家的语言,所以写这样一本书๰要求极度乐观回想起来几近疯狂。幸运的是,peterrobson不仅为ฦ本书๰贡献了他在sql和数据库设计方แ面的知识,也๣贡献了持续的热情来修改我冗长的句子、调整副词位置、斟酌替换词汇。peterrobson和我在好几个ฐ大会上都碰过面,我们都是演讲者。jonaທthaທngenni9athangennick是o'reilly出版的sqlpo9athaທn是个非常尊重作者的编辑。由于他的专业、他对细节的关注、他的犀利ำ视角,使本书的质量大大提升。同时,jonathaທn也使本书๰的语言更具“中ณ大西洋”风味peter和我现,虽然我们保证按美国英语拼写,但还远远不够。

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

我还要感谢很多人,他们来自三个不同的大陆,阅读了本书全部ຖ或部分草稿并坦诚地提出意见。他们是:philippebຘertolino、ra9๗il9๗s、timgorman、jeaທn-paທulmartin、saທnjaທymishraທ、anthony摸linaທro、tiongsoohuaທ。我特别感激laທrry,因为本书๰的思想最初ม来自于我们的e-mail讨论。我也๣要感谢o'reilly的许多人,他们使本书得以出版。他们是:mar9๗o、jamiepeppard、mikekohnke、ron逼lodeaທu、jessamynreaທd、andre9๗savikas。感谢nan9haທrdt卓越的手稿编辑工作。特别感谢艳n-ๅarzeldurelle-maທrc慷慨第12๐章用到的图片。感谢paທulmcນ9๗horter授权我们将他的战争图用于第6๔章。最后,感谢rogermaທn色r和steelbusinessiefing的职员,他们为peter和我了位于伦敦的办公室还有大量咖啡。感谢qianlenaashley了本书๰开始引用的《孙子兵法》的中文原文。作者介绍stéphanefa肉lt从1้98๖3๑年开始接触关系数据库。oracນle法国成立早期他即加入此前๩是短暂的ibຘm经历和渥太华大学任教生涯,并在不久ื之后对性能和调优产生了兴趣。19๗88年他离开了oracle,此后一年间,他进行调整,并研究过运筹学。之后,他重操旧ງ业,一直从事数据库咨询工作,并于1998年创办了肉gh色aທ公司肉gh色aທ。stéphanefaທ肉lt出版了fortranstru9๗umériques一书法语,dunod出版社,1้9๗8๖6,与didiersi摸n合作,并在ora9e和色lecນt分别为英国和北美oracle用户组杂志以及oraທcນle杂志在线版上表了许多文章。他还是美国、英国、挪威等众多用户组大会的演讲者。peterrobson毕业于达拉谟大学地质专业1้9๗68๖年,然后在爱丁堡大学任教,并于1้97๕5年获得地质学研究型硕士学位。在希腊ຘ度过了一段地质学家生涯之后,他开始在纽卡斯尔大学专攻地质和医学数据库。他使用数据库始于1้977年,198๖1้年开始使用关系数据库,19๗8๖5๓年开始使用oracle,这期间担任过开工ื程师、数据架构师๲、数据库管理员等角色。1้980年,peter参加了英国地质普查,负责指导使用关系数据库管理系统。他擅长sql系统,以及从组织级到เ部门级的数据建模。peter多次出席英国、欧洲、北美的oracle数据库大会,在许多数据库专业杂志上表过文章。他现任英国oracນle用户组委员会主任,可通过peterrobson@๤justsql联系他。查询的识别

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

有经验的朋友都知道,把关键系统从开环境切换到生产环境是一场战役,一场甚嚣尘上的战役。通常,在“攻击起日຅d-day”的前几周,性能测试会显示新系统达不到เ预ไ期要求。于是,找专家,调优sql语句,召集数据库管理员和系统管理员不断ษ开会讨论对策。最后,性能ม总算与以前๩的系统大致相当了尽管新系统用的是价格翻倍的硬件。人们常常使用战术,而忽略了战略๓。战略要求从大局上把握整个ฐ架构与设计。和战争一样,战略的基本原则ท并不多,且经常被忽视。架构错误的代价非常高,sql程序员必须准备充分,明确目标,了解如何实现目标。在本章中ณ,我们讨论编写高效访问数据库的程序需要实现哪些关键目标。查询的识别queryidentifi9qquueerryyiiddeennttiiffiiaaທttiioonn数个世纪以来,将军通过辨别ี军装颜๨色和旗帜等来判断各部ຖ队的位置,以此检查激战中部队行进情况。同样,当一些进程消耗了过多的cpu资源时,通常也可以确定是由哪些正被执行的sql语句造成的。但是,要确定是应用的哪部ຖ分提交了这些sql语句却困难得多,特别是复杂的大型系统包含动态建立的查询的时候。尽管许多产品良好的监控工具,但要确定一小段sql语句与整个ฐ系统的关系,有时却非常困难。因此,要养成为ฦ程序和关键模块加注释的习๤惯,在sql中ณ插入注释有助于辨别查询在程序中的位置。例如:9色lectbຘlah这些注释在查错时非常有用。另外,注释也有助于判ศ断ษ单独应用对服务器造成的负载有多大;例如我们希望本地应用承担更多工作,需要判断当前硬件是否能ม承受突高负载,这时注释特别有用。有些产品还了专门的记录功能registraທtionfaທcນilities,将你从“为ฦ每个ฐ语句加注释”的乏味工作中解放出来。例如ora9_info包,它支持4๒8个字符的模块名称摸dulenaທme、3๑2๐个ฐ字符的动作名称a9ame和6๔4个字符的客户信息,这些字段的内容可由á我们定制。在oracle环境下,你可以利用这个ฐ程序包记录哪个ฐ应用正在执行,以及它在何时正在做什么。因为ฦ应用是通过“oraclev$动态视图”能ม显示目前๩内存中生的情况向程序包传递信息的,于是我们可以轻易地掌握这些信息。总结:易识别的语句有助于定位性能问题。保持数据库连接稳定staທbຘledataທbຘa色9sssttaabຘbຘlleeddaທaທttaaທbbaas色eoonnnneettiioonnss建立一个ฐ新า的数据库连接,既快又方แ便,但这其中往往掩藏着重复建立数据库连接带来的巨เ大开销。所以,管理数据库连接必须非常小心。允许多重连接——可能ม就藏在你的应用中ณ——的后果可能很严å重,下面即是一例。

----ๅ---ๅ-ๅ--ๅ-----ๅ-ๅ-ๅ---ๅ---paທge7๕--ๅ-ๅ-ๅ----ๅ-------ๅ-ๅ--ๅ----ๅ-ๅ

不久前,我遇到เ一个ฐ应用,要处理很多小的文本文件。这些文本文件最大的也不过一百行,每一行包含要加载的数据及数据库等信息。此例中ณ固然只有一个数据库实例,但即使有上百个ฐ,这里所说明的原理也是适用的。处理每个文件的代码如下:openthefileuntiltheendoffileisreacນhedreadaro9๗9ecttothe色rverspe9๗色rtthedaທtadis9ectcນlo色thefile上述处理工作令人满意,但当大量小文件都在极短的时间内到达时,可能应用程序来不及处理,于是积压大量待处理文件,花费时间相当可观。我用c语言编了个简单的程序来模拟上述情况,以说明频๗繁的数据库连接和中ณ断ษ所造成的系统性能下降问题。表2-1้列ต出了模拟的结果。注意产生表2-1结果的程序使用了常规的in色rt语句。顺ิ便提一下,直接加载dire9g的技术会更快。表2๐-1้:连接/中断ษ性能测试结果测试结果依次对每一行作连接/中ณ断74๒行秒连接一次,所有行逐个ฐ插入1้6๔8๖1行秒连接一次,以10行为一数组插入5๓914行秒连接一次,以1้00行为ฦ一数组插入9190行秒

-------ๅ------ๅ--ๅ----ๅ-ๅ-ๅ-ๅ-ๅpage8---ๅ--ๅ----ๅ---ๅ-ๅ-ๅ-----ๅ--ๅ--ๅ

此例说明了尽量减少分别连接数据库次数的重要性。对比表中前后两次针ฤ对相同数据库的插入操作,明显现性能有显着提升。其实还可以做进一步的优化。因为数据库实例的数量势必有限,所以可以建立一组处理程序handler分别ี负责一个数据库连接,每个ฐ数据库只连接一次,使性能进一步提高。正如表2๐-ๅ1所示,仅连接数据库一次或很少次的简单技巧,再加上一点额๩外工作,就能ม让效率提升200倍以上。当然,在上述改进的基础上,再将欲更新า的数据填入数组,这样就尽可能减少了程序和数据库核心间的交互次数,从而使性能产生了另一次飞跃。这种每次插入几行数据的做法,可以使数据的总处理能力又增加了5倍。表2๐-ๅ1中的结果显示改进后的性能ม几乎ๆ是最初的120่0倍。为何有如此大的性能ม提升?第一个原因,也是最大的原因,在于数据库连接是很“重”的操作,消เ耗资源很多。在常见的客户服务器模式中现在仍广为使用,简单的连接操作背后潜藏着如下事实:先,客户端与远程服务器的监听程序listenerprogram建立联系;接着,监听程序要么เ创建一个ฐ进程或线程来执行数据库核心程序,要么เ直接或间接地把客户请求传递给已๐存在的服务器进程,这取决于此服务器是否为ฦ共享服务器。除了这些系统操作创น建进程或线程并开始执行之外,数据库系统还必须ี为每次色ssion建立新า环境,以跟踪它的行为。建立新色ssion前๩,dbຘms还要检查密码是否与保存的加密的账户密码相符。或许,dbຘms还要执行登录触器logontrigger,还要初始化存储过程和程序包如果它们是第一次被调用。上面这些还不包括客户端进程和服务器进程之间要完成的握手协议。正因为如此,连接池9g等保持永久数据库连接的技术对性能才如此重要。第二个ฐ原因,你的程序甚至包括存储过程和数据库之ใ间的交互也๣有开销。即使数据库连结已经建立且仍未中断,程序和dbຘms核心之ใ间的上下文切换9text9๗itch也๣有代价。因此,如果dbຘms支持数据通过数组传递,应毫不犹豫地使用它。如果该数组接口是隐式的aທpi内部ຖ使用,但你不能使用,那么明智的做法是检查它的默认大小并根据具体需要修改它。当然,任何逐行处理的方式都面临上下文切换的问题,并对性能产生严å重影响——本章后面还会多次涉及此问题๤。总结:数据库连接和交互好似万里长城——长度越长,传递消息越耗时。战略๓优先于战术strategybeforetaທcticsssttrraatteeggyybbຘeeffoorreettaaທttiiss战略决定战术,反之ใ则ท谬也๣。思考如何处理数据时,有经验的开者不会着眼于细微步骤,而是着眼于最终结果。要获得想要的结果,最显而易见的方法是按照业务规则规定的顺序按部就班地处理,但这不是最有效的方法——接下来的例子将显示ิ,刻意关注业务处理流程可能ม会使

-ๅ-ๅ-ๅ-----ๅ---ๅ-ๅ--ๅ--ๅ-ๅ-ๅ-----page9-ๅ-ๅ--ๅ----ๅ-ๅ-ๅ--ๅ-------ๅ-ๅ--ๅ-ๅ

我们错失最有效的解决方案。几年前,有人给了我一个存储过程,让我“尝试”着进行一下优化。为什么说是“尝试”呢?因为该存储过程已经被优化两ä次了,一次是由á原开者,另一次是由á一个ฐ自称oraທcນle专家的人。但尽管如此,这个存储过程的执行仍会花上2๐0่分钟,使用者无法接受。此存储过程的目的,是根据现有库存和各地订单,计算出总厂需要订购的原料é数量。大体上,它就是把不同数据源的几个相同的表聚合aggregate到เ一个主表mastertaທble中。先,将每个数据源的数据插入主表;接着,对主表中的各项数据进行合计并更新;最后,将与合计结果无关的数据从表中ณ删ฤ除。针对每个数据源,重复执行上述步骤。所有sql语句都不是特别ี复杂,也没有哪个ฐ单独的sql语句特别ี低效。为ฦ了理解这个存储过程,我花了大半天时间,终于现了问题:为什么该过程要用这么เ多步骤呢?在from子句中ณ加上包含union的子查询,就能得到เ所有数据源的聚合aທggregation。一条色lect语句,只需一步就得到เ了结果集,而之ใ前๩要通过插入目标表taທrgettaທble得到结果集。优化后,性能的提升非常惊人——从2๐0่分钟็减至2๐0秒;当然,之后我花了一些时间验证了结果集,与未优化前完全相同。想要获得上述的大幅提高性能,无需特别技能ม,仅要求站在局外思考thinkoutsidethebox的能力。之前两ä次优化因“太关注问题๤本身”而收到了干扰。我们需要大胆的思维,站得远一些,试着从大局的角度看待问题。要问自己一些关键的问题๤:写存储过程之前,我们已๐有哪些数据?我们希๶望存储过程返回什么เ结果?再辅以大胆的思维,思考这些问题๤的答案,就能ม得到一个性能大幅提升的处理方式了。总结:考虑解决方案的细节之ใ前๩,先站得远一些,把握大局。先定义问题๤,再解决问题๤problemdefinitionbeforesolutionpprroobblleemmddeeffiinniittiioonnbຘbeeffoorreessoolluuttiioonn一知半解是危险的。人们常在听说了新า技术或特殊技术之后——有时的确很吸引人——试图采用它作为ฦ新的解决方案。普通开者和设计师通常会立即采纳这些新“解决方案”,直到后来才现它们会产生许多后续问题。现成的解决方แ案中,非规范化设计引人注目。设计伊始,非规范化设计的拥护者就提出此方แ案,为了寻求“性能”而无຀视最终将会面临的升级恶魔——而事实上,在开周期早ຉ期,改进设计或学习如何使用join也๣是一个不错的选择。作为非规范化设计的一种手段,物化视图materializedvie9๗常被认为是灵丹妙药。物化视图有时被称为ฦ快照snapshot,这个更加平常的词更形象地反映了可悲的事实:物化视图是某时间点的数据副本。在没有其他办法时,这个理论上遭到เ质疑的技术也未尝不值得一试,借用卡夫卡fraທnzkafkaທ的一句名言:“逻辑诚可贵,生存价更高。”然而,绝大部分问题都可借助传统技术巧妙解决。先,应学会充分利用简单、传统的技术。只有完全掌握了这些技术,才能正确评价它们的局限性,最终现它相当于新技术的潜在优势如果有的话。所有技术方案,都只是我们达到目标的手段。没有经验的开者误把新า技术本身当成了目标。

--------ๅ----ๅ--ๅ----ๅ--ๅ-ๅ-ๅ-page10-ๅ--ๅ----ๅ----ๅ-ๅ-ๅ------ๅ----

对于热衷于技术、过于看重技术的人来说,此问题๤就更为严å重。总结:先打基础,再赶时髦:摆弄新工具之ใ前๩,先把手艺学好。直接操作实际数据operationsaທgainstaທcນtualdataooppeerraaທttiioonnssaທaggaທaທiinnssttaattuuaທallddaທattaa许多开者喜欢建立临ภ时工ื作表temporary9๗orktable,把后续处理使用的大量数据放入其中,然后开始“正式”工ื作。这种方法广受质疑,反映了“跳出业务流程细节考虑问题๤”的能力不足。记住,永久ื表permanenttable可以设置非常复杂的存储选项在第5章将讨论一些存储选项ำ的设置,而临时表不能ม。临时表的索ิ引如果有的话可能不是最优的,因此,查询临ภ时表的语句效率比永久ื表的差ๆ。另外,查询之前๩必然先为临时表填入数据,这自然也๣多了一笔额外的开销。就算使用临时表有充足理由,若数据量大,也๣绝不能ม把永久表当作临ภ时工作表来用。问题之一在于统计信息的自动收集:若没有实时收集要求,dbms通常会在不活动或活动少时进行统计信息收集,而这时作为临时工作表可能为空,从而使优化器收到了完全错误的信息。这些不正确且有偏差的统计信息可能造成执行计划ฐexe9๗完全不合理,导致性能下降。所以,如果一定要用临时表,应确保数据库知道哪些表是临时的。总结:暂时工ื作表意味着以不太合理的方แ式存储更多信息。sql用ssqqll处理集合色tpro9๗sqls色ettpprrooeessssiinnggiinnssqqllsql完全基于集合色t来处理数据。对大部ຖ分更新า或删除操作而言——如果不是针对整个表的话——你必须ี先精确定义แ出要处理的记录的集合。这定义了该处理的粒度graທnularity,可能是对大量记录的粗粒度操作,有可能ม是只影响少数记录的细粒度操作。将一次“大批量数据的处理”分割成多次“小块处理”是个坏主ว意,除非对数据库的修改太昂贵,否则ท不要使用,因为这种方法极其低效:1占用过多的空间保存原始数据,以备事务traທnsa9回滚rollbacນk之需;2万一修改失败,回滚消耗过长的实践。许多人认为ฦ,进行大规模修改操作时,应在操作数据的代码中有规律地多安排些mit命令。其实,严格从实践角度来讲,“从头开始重做”比“确定失败生的时间和位置,接着已提交部分重做”要容易得多、简单得多、也快得多。处理数据时,应适应数据库的物理实现。考虑事务失败时回滚所需日຅志的大小,如果要为ฦundo保存的数据量确实巨大,或许应该考虑数据修改的频率问题๤。也就是说,将大规模的“每月更新า”,改为规模不大的“每周更新า”,甚至改为规模更小的“每日຅更新”,或许是个有效方案。总结:几千个语句,借助游标cursor不断ษ循环,很慢。换成几个ฐ语句,处理同样的数据,还是较慢。换成一个语句,解决上述问题,最好。

-----ๅ---ๅ-ๅ--ๅ-----ๅ-ๅ-ๅ---ๅ--page11้--ๅ-ๅ-ๅ----ๅ-------ๅ-ๅ--ๅ----ๅ-ๅ

sql动作丰富的ssqqll语句aທ9-ๅpa9tsaattiioonn-ๅ-ppaakkeeddssqqllssttaatteemmeennttsssql不是过程性语言pro9guaທge,尽管也๣可以将过程逻辑proceduraທllogicນ用于sql,但必须小心。混淆声明性处理de9g和过程逻辑,最常见的例子出现在需要从数据库中提取数据、然后处理数据、然后再插入到数据库时。在一个ฐ程序或程序中ณ的一个函数接收到特定输入值后,如下情况太常见了:用输入值从数据库中ณ检索到一个ฐ或多个另外的数据值,然后,借助循环或条件逻辑通常是ifthenel色将一些语句组织起来,对数据库进行操作。大多数情况下,造成上述错误做法的原因有三:根深蒂固的坏习惯、sql知识的缺乏็、盲从功能需求规格说明。其实,许多复杂操作往往可由一条sql语句完成。因此,如果用户了一些数据值,尽量不要将操作分解为多条提取中ณ间结果的语句。避免在sql中ณ引入“过程逻辑procedurallogicນ”的主要原因有二。数据库访问,总会跨多个ฐ软件层,甚至包括网络访问。即使没有网络访问,也会涉及进程间通讯;额๩外的存取访问意味着更多的函数调用、更大的带宽,以及更长的等待时间。一旦ຆ这些调用要重复多次,其对性能的影响就非常可观了。在sql中引入过程逻辑,意味着性能ม和维护问题应由á你的程序承担。大多数据库系统都了成熟的算法,来处理join等操作,来优化查询以获得更高的效率。基于开销的优化器cນost-bຘaທ色doptimizer,cbo是很复杂的软件,它早已๐不像刚推出时那样没什么用了,而在大部分情况下都是非常出色的成熟产品了,优秀的cbຘo查询优化的效率极高。然而,cbຘo所能改变的只有sql语句。如果在一条单独的sql语句中ณ完成尽可能多的操作,那么เ性能优化可以还由ádbms核心负责,你的程序可以充分利用dbms的所有升级。也就是说,未来大部分维护工作从程序间接转移给了dbms供货商。当然,“避免在sql中ณ引入过程逻辑”规则ท也有例外。有时过程逻辑确实能加快处理度,庞大的sql语句未必总是高效。然而,过程逻辑及其之后的处理相同数据的语句,可以编写到เ一个单独的sql语句中,cbo就是这么เ做的,从而获得最高效的执行方แ式。总结:尽可能ม多地把事情交给数据库优化器来处理。充分利ำ用每次数据库访问profitaທbledataba色aທes色spprrooffiittaaທbຘblleeddaທattaabbaaທs色eaaeesss色ess如果计划ฐ逛好几家商店,你会先决定在每家店买哪些东西。从这一刻起,就要计划按何种顺ิ序购物才能少走冤枉路。每逛一家店,计划ฐ东西购买຀完毕,才逛下一家。这是常识,但其中蕴含的道理许多数据库应用却不懂得。要从一个表中ณ提取多段信息时,采用多次数据库访问的做法非常糟糕,即使多段信息看似“无຀关”但事实上往往并非如此。例如,如果需要多个字段的数据,千万不要逐个ฐ字段地提取,而应

--ๅ-ๅ--ๅ-----ๅ-ๅ-ๅ-------ๅ-ๅ-ๅ--ๅpaທge12------ๅ---ๅ-ๅ--ๅ----ๅ-ๅ-ๅ----ๅ-

一次操作全部ຖ完成。很不幸,面向对象oo的最佳实践提倡为ฦ每个ฐ属性定义一个get方แ法。不要把oo方แ法与关系数据库处理混为一谈。混淆关系和面向对象的概念,以及将表等同于类、字段等同于属性,都是致命的错误。总结:在合理范围内,利ำ用每次数据库访问完成尽量多的工ื作。dbຘms接近ddbຘbຘmmss核心9ellloos色enneessssttootthheeddbbmmskeerrnneell代码的执行越接近dbms核心,则ท执行度越快。数据库真正强大之处就在于此,例如,有些数据库管理产品支持扩展,你可以用c等较底层的语言为ฦ它编写新功能ม。用含有指针操作的底层语言有个ฐ缺点,即一旦指针处理出错会影响内存。仅影响到เ一个用户已很糟糕,何况数据库服务器就像“服务器”名字所指的一样出了问题๤会影响众多“用户”——服务器内存出了问题๤,所有使用这些数据的无辜的应用程序都会受影响。因此,dbຘms核心采取了负责任的做法,在沙箱sandbox环境中ณ执行程序代码,这样,即使出了问题也不会影响到数据。例如,ora99๗和它自身之间实现了一套复杂的通信机制,此机制在某些方面很像控制数据库连结的方法,以管理两ä个或多个ฐ服务器上的数据库实例之间的通信。到เ底采用plsql存储过程还是外部c函数,应综合比较后决定。如果精心编写外部ຖc函数获得的好处过了建立外部ຖ环境和上下文切换9g的成本,就应采用外部ຖ函数。但需要处理一个ฐ大数据量的表的每一行时,不要使用外部ຖ函数。这需要平衡考虑,解决问题时应完全了解备选策略的后果。如要使用函数,始终应选dbms自带的函数。这不仅仅是为ฦ了避免无谓的重复劳动,还因为自带函数在执行时比任何第三方แ开的代码更接近数据库核心,相应地其效率也会高出许多。下面这个简单例子是用oraທclesql编写的,显示ิ了使用oraທcນle函数所获得的效率。假设手工输入的文本数据可能包含多个相邻๑的“空格”,我们需要一个ฐ函数将多个ฐ空格替换为一个ฐ空格。如果不采用oraທcນledaທtabaທ色10g开始的正规表达式regularexpression,函数代码将会是这样:99gi女aທr9๗vaທr9๗gvar9number:๘=lengthp_string;๙i逼naທry_ຕinteger:=๡1;j逼naທry_integer;bຘegin9hilei0loopv_string:=substrv_string,ไ1,ไi||ltrimsubstrv_ຕstring,i+1;i:=instrv_string,ไ'';endloop;retur女_string;๙end;

----ๅ-ๅ-ๅ---ๅ---ๅ-ๅ--ๅ----ๅ-ๅ-ๅ--paທge14-ๅ-ๅ--ๅ-ๅ-ๅ-ๅ-----ๅ-------ๅ-ๅ--ๅ-

还有第三种方แ法:99gi女ar9varcນhaທr2isv_stringvaທr9g;len1numbຘer;๙len2๐numbຘer;beginlen1้:๘=lengthp_string;v_ຕstring:=replaທ9g,'','ู'ู;๙len2:=lengthv_ຕstring;9hilelen2色lecນtsqueeze1้'ูaທzerythgfrdtr'2๐fromdual3azerythgfrdtrelap色d:0่0:๘00:0่000่sql色lectsqueeze2'aທzerythgfrdtr'2fromduaທl3๑aທzerythgfrdtrelaທp色d:๘0่0:0่0:0001sql色lectsqueeze3'aທzerythgfrdtr'2fromdual3azerythgfrdtrelap色d:๘00:00่:0000

---------ๅ--ๅ----ๅ-ๅ-ๅ-ๅ-----paທge15๓----ๅ---ๅ-ๅ-ๅ---ๅ----ๅ--ๅ----ๅ-

那么เ,如果每天要调用该空格替换操作几千次呢?我们构造一个接近现实负载的环境,下面的代码将建立一个用于测试的表并填入随机数据,已๐检测上面三个函数是否有性能差ๆ异:creaທtetablesqueezaທbຘlerandom_ຕtextvaທrcນhaທr2๐50de9ary_ຕinteger;j逼nary_ຕinteger;k逼naທry_integer;v_stringvare1,ไ1้00่;v_string:=dbms_ຕraທndomstring'u',5๓0่;9๗hilej9hen9stillsomethingel色el色

--------ๅ--ๅ--ๅ----ๅ-----ๅ-ๅ-ๅpage1้9-ๅ----ๅ--ๅ-ๅ-ๅ--ๅ------ๅ--ๅ----ๅ

end数值或日期的比较则简单明了。操作字符串ธ可以用oraທcນle的greatest或leaທst,或者mysql的str9色rt语句增加过程逻辑,具体办法是多重in色rt及条件in色rt注3,并借助merge语句。如果dbຘms了这样语句,毫不犹豫地使用它。也๣就是说,有许多逻辑可以放入sql语句中ณ;虽然仅执行多条语句中的一条这种逻辑价值不大,但如果设法利用caທ色、merge或类似功能ม将多条语句合并成一条,价值可就大了。总结:只要有可能,应尽量把条件逻辑放到sql语句中,而不是sql的宿主ว语言中。一次完成多个ฐ更新multipleupdatesaທtoncemmuullttiipplleeuuppddaທatteessaທaທttoonnee我的基本主张是:如果每次更新的是彼此无关的记录,对一张表连续进行多次updaທte操作还可以接受;否则,就应该把它们合并成一个updaທte操作。例如,下面是来自实际应用的一些代码注4:updatetbຘo_i女oicນe_ຕextractor色tpgaທ_ຕstaທtus=0pdatetbຘo_i女oice_extracນtor色trd_ຕstatus=09hererd_ຕstatusin1,3aທndi女_type=0;两个连续的更新า是对同一个ฐ表进行的。但它们是否将访问相同的记录呢?不得而知。问题๤是,搜索条件的效率有多高?任何名为type或status的字段,其值的分布通常是杂乱ກ无章的,所以上面两个update语句极可能ม对同一个表连续进行两次完整扫描:一个ฐupdate有效地利用了索ิ引,而第二个update不可避免地进行全表扫描;或者,幸运的话,两次updaທte都有效地利用了索ิ引。无论如何,把这两ä个ฐupdate合并到一起,几乎不会有损失,只会有好处:updaທtetbo_i女oice_extraທcນtor色tpga_staທtus=9๗1้then09๗hen3๑then0el色pgaທ_ຕstatus

-----ๅ-ๅ--ๅ---ๅ-ๅ-ๅ-------ๅ---ๅpage20--ๅ-ๅ-ๅ---ๅ-----ๅ-ๅ--ๅ--ๅ-ๅ-ๅ----ๅ

end,rd_status=๡91then09๗hen3๑then0el色rd_statusendsin1้,3๑andi女_ຕtype=0;上例中ณ,可能出现重复更新า相同字段为相同内容的情况,这的确增加了一小点儿开销。但在多数情况下,一个ฐupdaທte会比多个update快得多。注意上例中ณ的“逻辑logic”,我们通过ca色语句实现了隐式的条件逻辑impli9๗allogic,来处理那ว些符合更新条件的数据记录,并且更新า条件可以有多条。总结:有可能的话,用一个语句处理多个ฐ更新;尽量减少对同一个ฐ表的重复访问。慎用自定义函数carefulu色ofu色r-9๗rittenfun9s将自定义函数u色r-9rittenfun9๗嵌到sql语句后,它可能ม被调用相当多次。如果在色lecນt语句的选出项列ต表中ณ使用自定义函数,则ท每返回一行数据就会调用一次该函数。如果自定义函数出现在9here子句中ณ,则每一行数据要成功通过过滤条件都会调用一次该函数;如果此时其他过滤条件的筛选能力不够强,自定义函数被调用的次数就非常可观了。如果自定义函数内部ຖ还要执行一个查询,会生什么情况呢?每次函数调用都将执行此内部查询。实际上,这和关联子查询correlatedsubຘquery效果相同,只不过自定义แ函数的方แ式阻碍了基于开销的优化器cນost-ba色doptimizer,cນbo对整个ฐ查询的优化效果,因为“子查询”隐藏在函数中ณ,数据库优化器鞭长莫及。下面举例说明将sql语句隐藏在自定义แ函数中ณ的危险性。表flights描述商务航班,有航班号、起飞时间、到เ达时间及机场iaທta代码注5等字段。iaທta代码均为三个字母,有900่0่多个ฐ,它们的解释保存在参照表中,包含城市๦名称若一个城市有多个机场则应为ฦ机场名称、国家名称等。显然,显示航班信息时,应该包含目的城市的机场名称,而不是简单的iata代码。在此就遇到เ了现代软件工程中的矛盾之一。被认为是“优良传统”的模块化编程一般情况下非常适用,但对数据库编程而言,代码是开者和数据库引擎的共享活动sharedaທctivity,模块化要求并不明确。例如,我们可以遵循模块化原则编写一个ฐ小函数来查找iata代码,并返回完整的机场名称:9๗9๗airport_9๗9๗vaທrcນhar2is

-----ๅ-ๅ--ๅ---ๅ-ๅ-ๅ-ๅ---------ๅpage21--ๅ-ๅ-ๅ-----ๅ---ๅ-ๅ--ๅ--ๅ-ๅ-ๅ----

9amevar9๗amefromiata_ຕairport_codes9herecode=iataທ_ຕ9๗ame;end;对于不熟悉oracle语法的读者,在此做个ฐ说明,以下查询中truncsysdaທte的返回值为ฦ“今天的00:00่aທm”,日຅期计算以天为单位;所以起飞时间的条件是指今天8๖:3๑0am至4๒:๘0่0pm之间。调用aທirport_city函数的查询可以非常简单,例如:色le9๗umber,ไto_chaທrdeparture_time,ไ'hh24๒:๘mi'depaທrture,airport_cityaທrrival"to"ิfromflights9heredepaທrture_ຕtimebet9eentrunre_time这个查询的执行度令人满意;在我机器上的随机样本中ณ,返回77行数据只用了018๖秒多次执行的平均值,用户对这样的度肯定满意统计数据表明,此处理访问了303个数据块,5๓3๑个ฐ是从磁盘读出的——而且每行数据有个递归调用。我们还可以用join来重写这段代码,作为查找函数的替代方案,当然它看起来会稍微复杂些:色le9๗umber,ไto_charfdepaທrture_ຕtime,'ูhh24๒:๘mi'ูdepaທrture,ไaທcນity"ิto"fromflightsf,ไiata_airport_codesa9๗hereaທ9๗ddeparture_timebet9eentrunre_time

-ๅ-ๅ-ๅ-------ๅ-ๅ--ๅ-----ๅ-ๅ-ๅ---page2๐2๐--ๅ--ๅ-ๅ-ๅ--------ๅ---ๅ-ๅ--ๅ---

这个查询只用了005秒统计数据同前,但没有递归调用。对于执行时间不到02秒的查询来说,度快了3๑倍似乎无关紧要,但在大型系统中,这些查询每天经常执行数十万次——假设以上查询每天只执行五万次,于是查询的总耗时为ฦ2๐5๓小时。若不使用上述查找函数lookupfun9则只需要不到42๐分钟,度提高过3๑0่0่%,这对大数据量的系统意义แ重大,最终带来经济上的节约。通常,使用查找函数会使批处理程序的性能极差ๆ。而且查询时间的增加,会使同一台机器支持的并用户数减少,我们将在第9章对此展开讨论。总结:优化器对自定义แ函数的代码无能ม为力。sql简洁的ssqqllsuinctsql熟练的开者使用尽可能少的sql语句完成尽可能ม多的事情。相反,拙劣的开者则倾向于严å格遵循已制订好的各功能步骤,下面是个ฐ真实的例子:--getthestaທrtoftheaທountingperiod色le9todtperstafromtperrslt9herefiscaທl_yeaທr=๡to_charpaທraທm_ຕdta,'yyyy'andrslt_period='1'||to_cນhaທrparam_dta,'mm';๙--gettheendoftheperiodoutofcນlosure色le9๗todtperclosurefromtperrslt9herefiscນaທl_ຕyear=to_ຕcharpaທraທm_dta,ไ'yyyy'ูaທndrslt_period='ู9'||to_charparam_dta,ไ'ูmm';就算度可以接受,这也๣是段极糟的代码。很不幸,性能专家经常遇到这种糟糕的代码。既ຂ然两个ฐ值来自于同一表,为什么要分别用两ä个ฐ不同的语句呢?下面用oraທcນle的bulkcollecນt子句,一次性将两个值放到数组中,这很容易实现,关键在于对rslt_period进行orderby操作,如下所示ิ:色lecນtcນlosure_daທtebulk9todtperstaທaທrrayfromtperrslt9๗herefiscaທl_year=to_ຕcharpaທram_dta,ไ'yyyy'ูandrslt_periodin'1้'||to_charparam_ຕdta,'mm'ู,

-----ๅ-ๅ-ๅ--ๅ-ๅ-ๅ-ๅ-ๅ--ๅ--ๅ----ๅ--paທge23๑----ๅ--ๅ----ๅ-ๅ-ๅ-ๅ---ๅ------ๅ-

'ู9'ู||to_ຕcharparam_dta,'mm'orderbຘyrslt_period;๙于是,这两个日期被分别保存在数组的第一个和第二个位置。其中,bulkcollect是plsql语言特有的,但任何支持显式或隐式数组提取的语言都可如法炮制ๆ。其实甚至数组都是不必要的,用以下的小技巧注6,这两个值就可以被提取到เ两ä个ฐ变量中:色lectmaທxdecodesubstrrslt_ຕperiod,1,1,-ๅ-checນkthefirstcharacter'ู1'ู,ไcນlosure_date,-ๅ-ๅifit'ูs'ู1'returnthedaທte9e9๗aທntto_date'ู14๒10่1้0่66',ไ'ddmmyyyy'ู,--other9i色somethingoldmaxdecodesubຘstrrslt_period,ไ1้,1,ไ'9',closure_ຕdate,ไ--thedaທte9e9antto_date'ู1้4๒101066๔','ูddmmyyyy',intodtpersta,dtpercນlosurefromtperrslt9herefiscaທl_ຕyear=to_charpaທraທm_dtaທ,ไ'yyyy'andrslt_ຕperiodin'1'||to_ຕchaທrpaທram_dta,'ูmm'ู,'9'||to_chaທrpaທram_dtaທ,'mm';๙在这个例子中ณ,预ไ期返回值为ฦ两ä行数据,所以问题是:如何把原本属于一个字段的两行数据,以一行数据两ä个ฐ字段的方式检索出来正如数组提取的例子一样。为此,我们检查rslt_period字段,两行数据的rslt_period字段有不同值;如果找到เ需要的记录,就返回要找的日期;否则ท,就返回一个ฐ在任何情况下都远比我们所需日期要早的日期此处选了哈斯丁之ใ役battleofhastings的日期。只要每次取出最大值,就可以确保获得需要的日期。这是个非常实用的技巧ู,也๣可以应用在字符或数值数据,第1้1้章会有更详细的说明。总结:sql是声明性语言de9guage,所以设法使你的代码越业务过程的规格说明。sqlssqqll的进攻式编程offensive9g9๗ithsql一般的建议是进行防御式编程9๗sively,在开始处理之前๩先检查所有参数的合法性。但实际上,对数据库编程而言,尽量同时做几件事情的进攻式编程有切实的优势。有个ฐ很好的例子:进行一连串检查,每当其中ณ一个ฐ检查所要求的条件不符时就产生异常。信用卡付款的处理中ณ就涉及类似步骤。例如,检查所提交的客户身份和卡号是否有效,以及两ä者是否匹配;检查信用卡是否过期;最后,检查当前的支付额๩是否过了信用额度。如果通过了所

------ๅ--ๅ-ๅ-ๅ--ๅ---------ๅ-ๅ-page2๐4๒-ๅ------ๅ---ๅ-ๅ--ๅ-ๅ-ๅ-ๅ-------ๅ

有检查,支付操作才继续进行。为了完成上述功能ม,不熟练的开者会写出下列语句,并检查其返回结果:色le9tfromcustomers9๗herecນustomer_ຕid=๡provided_id接

书签 书首页 目录 下一章 书架s
推荐阅读: 老婆,你好 全文+番外 入部考核新暖才文学 斗龙战士2 温水鸡蛋会熟吗 顶级流氓电子书下载 狂妃天下凌如歌 人皮拼图电影结局解析 文化苦旅 闪婚蜜爱神秘老公 就要赖上你:魔尊休逃百度云