]]>一个关于优化SQL的文?/title>http://www.aygfsteel.com/liujw/articles/39143.html刘军?/dc:creator>刘军?/author>Tue, 04 Apr 2006 05:42:00 GMThttp://www.aygfsteel.com/liujw/articles/39143.htmlhttp://www.aygfsteel.com/liujw/comments/39143.htmlhttp://www.aygfsteel.com/liujw/articles/39143.html#Feedback0http://www.aygfsteel.com/liujw/comments/commentRss/39143.htmlhttp://www.aygfsteel.com/liujw/services/trackbacks/39143.html SQL语句Q?br />是对数据?数据)q行操作的惟一途径Q?br />消耗了70%~90%的数据库资源Q独立于E序设计逻辑Q相对于对程序源代码的优化,对SQL语句的优化在旉成本和风险上的代价都很低Q?br />可以有不同的写法Q易学,隄通。?br /> SQL优化Q?br />固定的SQL书写习惯Q相同的查询量保持相同Q存储过E的效率较高。?br />应该~写与其格式一致的语句Q包括字母的大小写、标点符受换行的位置{都要一臾b?br /> ORACLE优化器: 在Q何可能的时候都会对表达式进行评伎ͼq且把特定的语法l构转换成等Ll构Q这么做的原因是 要么l果表达式能够比源表辑ּh更快的速度 要么源表辑ּ只是l果表达式的一个等仯义结构?br />不同的SQLl构有时h同样的操作(例如Q? ANY (subquery) and IN (subquery)Q,ORACLE会把他们映射C个单一的语义结构。?br /> 1 帔R优化Q?br />帔R的计是在语句被优化时一ơ性完成,而不是在每次执行时。下面是索月薪大?000的的表达式: sal > 24000/12 sal > 2000 sal*12 > 24000 如果SQL语句包括W一U情况,优化器会单地把它转变成第二种。?br />优化器不会简化跨比较符的表辑ּQ例如第三条语句Q鉴于此Q应量写用帔R跟字D|较检索的表达式,而不要将字段|于表达式当中。否则没有办法优化,比如如果sal上有索引Q第一和第二就可以使用Q第三就难以使用。?br /> 2 操作W优化: 优化器把使用LIKE操作W和一个没有通配W的表达式组成的索表辑ּ转换Z个?”操作符表达式。?br />例如Q优化器会把表达式ename LIKE 'SMITH'转换为ename = 'SMITH' 优化器只能{换涉及到可变长数据类型的表达式,前一个例子中Q如果ENAME字段的类型是CHAR(10)Q 那么优化器不做Q何{换。?br />一般来讲LIKE比较难以优化。?br /> 其中Q?br />~~ IN 操作W优化: 优化器把使用IN比较W的索表辑ּ替换为等L使用?”和“OR”操作符的检索表辑ּ。?br />例如Q优化器会把表达式ename IN ('SMITH','KING','JONES')替换为?br />ename = 'SMITH' OR ename = 'KING' OR ename = 'JONES‘?br /> ~~ ANY和SOME 操作W优? 优化器将跟随值列表的ANY和SOME索条件用{h(hun)的同{操作符和“OR”组成的表达式替换。?br />例如Q优化器如下所C的W一条语句用W二条语句替换: sal > ANY (:first_sal, :second_sal) sal > :first_sal OR sal > :second_sal 优化器将跟随子查询的ANY和SOME索条件{换成由“EXISTS”和一个相应的子查询组成的索表辑ּ。?br />例如Q优化器如下所C的W一条语句用W二条语句替换: x > ANY (SELECT sal FROM emp WHERE job = 'ANALYST') EXISTS (SELECT sal FROM emp WHERE job = 'ANALYST' AND x > sal)
~~ ALL操作W优? 优化器将跟随值列表的ALL操作W用{h(hun)的?”和“AND”组成的表达式替换。例如: sal > ALL (:first_sal, :second_sal)表达式会被替换ؓQ?br />sal > :first_sal AND sal > :second_sal 对于跟随子查询的ALL表达式,优化器用ANY和另外一个合适的比较W组成的表达式替换。例如?br />x > ALL (SELECT sal FROM emp WHERE deptno = 10) 替换为: NOT (x <= ANY (SELECT sal FROM emp WHERE deptno = 10)) 接下来优化器会把W二个表辑ּ适用ANY表达式的转换规则转换Z面的表达式: NOT EXISTS (SELECT sal FROM emp WHERE deptno = 10 AND x <= sal)
~~ BETWEEN 操作W优? 优化器L用?gt;=”和?lt;=”比较符来等L代替BETWEEN操作W。?br />例如Q优化器会把表达式sal BETWEEN 2000 AND 3000用sal >= 2000 AND sal <= 3000来代ѝ?br /> ~~ NOT 操作W优? 优化器L试图化检索条件以消除“NOT”逻辑操作W的影响Q这涉及到“NOT”操作符的消除以及代以相应的比较q算W。?br />例如Q优化器下面的W一条语句用W二条语句代替: NOT deptno = (SELECT deptno FROM emp WHERE ename = 'TAYLOR') deptno <> (SELECT deptno FROM emp WHERE ename = 'TAYLOR') 通常情况下一个含有NOT操作W的语句有很多不同的写法Q优化器的{换原则是低쀜NOT”操作符后边的子句尽可能的简单,即可能会ɾl果表达式包含了更多的“NOT”操作符。?br />例如Q优化器如下所C的W一条语句用W二条语句代替: NOT (sal < 1000 OR comm IS NULL) NOT sal < 1000 AND comm IS NOT NULL sal >= 1000 AND comm IS NOT NULL
如何~写高效的SQL: 当然要考虑sql帔R的优化和操作W的优化啦,另外Q还需要:
1 合理的烦引设计: 例:表record?20000行,试看在不同的索引下,下面几个SQL的运行情况: 语句A SELECT count(*) FROM record WHERE date >'19991201' and date < '19991214‘ and amount >2000
语句B SELECT count(*) FROM record WHERE date >'19990901' and place IN ('BJ','SH')
语句C SELECT date,sum(amount) FROM record group by date 1 在date上徏有一个非聚集索引 AQ?25U? BQ?27U? CQ?55U? 分析Q?br />date上有大量的重复|在非聚集索引下,数据在物理上随机存放在数据页上,在范围查找时Q必L行一ơ表扫描才能扑ֈq一范围内的全部行。?br />2 在date上的一个聚集烦引?br />AQ(14U) BQ(14U) CQ(28U) 分析Q?br />在聚集烦引下Q数据在物理上按序在数据页上,重复g排列在一P因而在范围查找Ӟ可以先找到这个范围的h点,且只在这个范围内扫描数据,避免了大范围扫描Q提高了查询速度。?br />3 在placeQdateQamount上的l合索引 AQ(26U) CQ(27U) BQ(< 1U) 分析Q?br />q是一个不很合理的l合索引Q因为它的前导列是placeQ第一和第二条SQL没有引用placeQ因此也没有利用上烦引;W三个SQL使用了placeQ且引用的所有列都包含在l合索引中,形成了烦引覆盖,所以它的速度是非常快的。?br />4 在dateQplaceQamount上的l合索引 AQ?< 1U? BQ(< 1U) CQ(11U) 分析Q?br />q是一个合理的l合索引。它?yu)date作ؓ前导列,使每个SQL都可以利用烦引,q且在第一和第三个SQL中Ş成了索引覆盖Q因而性能辑ֈ了最优。?br /> ȝ1 ~省情况下徏立的索引是非聚集索引Q但有时它ƈ不是最佳的Q合理的索引设计要徏立在对各U查询的分析和预上。一般来_ 有大量重复倹{且l常有范围查询(between, >,< Q?gt;=,< =Q和order by、group by发生的列Q考虑建立聚集索引Q?br />l 常同时存取多列Q且每列都含有重复值可考虑建立l合索引Q在条g表达式中l常用到的不同D多的列上建立索,在不同值少的列上不要徏立烦引。比如在雇员 表的“性别”列上只有“男”与“女”两个不同|因此无必要建立索引。如果徏立烦引不但不会提高查询效率,反而会严重降低更新速度。?br />l合索引要尽量关键查询形成索引覆盖Q其前导列一定是使用最频繁的列。?br /> 2 避免使用不兼容的数据cdQ?br />例如float和INt、char和varchar、bINary和varbINary是不兼容的。数据类型的不兼容可能优化器无法执行一些本来可以进行的优化操作。例? SELECT name FROM employee WHERE salary Q?0000 在这条语句中,如salary字段是money型的,则优化器很难对其q行优化,因ؓ60000是个整型数。我们应当在~程时将整型转化成ؓ钱币?而不要等到运行时转化。?br /> 3 IS NULL 与IS NOT NULLQ?br />不 能用null作烦引,M包含null值的列都不会被包含在烦引中。即使烦引有多列q样的情况下Q只要这些列中有一列含有nullQ该列就会从索引中排 除。也是说如果某列存在空|即对该列徏索引也不会提高性能。Q何在WHERE子句中用is null或is not null的语句优化器是不允 许使用索引的。?br /> 4 IN和EXISTSQ?br />EXISTS要远比IN的效率高。里面关pdfull table scan和range scan。几乎将所有的IN操作W子查询改写Z用EXISTS的子查询。?br />例子Q?br />语句1 SELECT dname, deptno FROM dept WHERE deptno NOT IN (SELECT deptno FROM emp); 语句2 SELECT dname, deptno FROM dept WHERE NOT EXISTS (SELECT deptno FROM emp WHERE dept.deptno = emp.deptno); 明显的,2要比1的执行性能好很多?br />因ؓ1中对empq行了full table scan,q是很浪Ҏ(gu)间的操作。而且1中没有用到emp的INdexQ?br />因ؓ没有WHERE子句。?中的语句对empq行的是range scan。?br /> 5 IN、OR子句怼使用工作表,使烦引失效: 如果不生大量重复|可以考虑把子句拆开。拆开的子句中应该包含索引。?br /> 6 避免或简化排序: 应当化或避免对大型表q行重复的排序。当能够利用索引自动以适当的次序生输出时Q优化器避免了排序的步骤。以下是一些媄响因素: 索引中不包括一个或几个待排序的列; group by或order by子句中列的次序与索引的次序不一P 排序的列来自不同的表。?br />Z避免不必要的排序Q就要正地增徏索引Q合理地合ƈ数据库表Q尽有时可能媄响表的规范化Q但相对于效率的提高是值得的)。如果排序不可避免,那么应当试图化它Q如~小排序的列的范围等。?br /> 7 消除对大型表行数据的序存取Q?br />在 嵌套查询中Q对表的序存取Ҏ(gu)询效率可能生致命的影响。比如采用顺序存取策略,一个嵌?层的查询Q如果每层都查询1000行,那么q个查询p查询 10亿行数据。避免这U情늚主要Ҏ(gu)是对连接的列进行烦引。例如,两个表:学生表(学号、姓名、年??Q和选课表(学号、课E号、成l)。如果两个 表要做q接Q就要在“学号”这个连接字D上建立索引。?br />q可以用ƈ集来避免序存取。尽在所有的查列上都有烦引,但某些Ş式的WHERE子句优化器用顺序存取。下面的查询强q对orders表执行顺序操作: SELECT Q FROM orders WHERE (customer_num=104 AND order_num>1001) OR order_num=1008 虽然在customer_num和order_num上徏有烦引,但是在上面的语句中优化器q是使用序存取路径扫描整个表。因个语句要索的是分ȝ行的集合Q所以应该改为如下语句: SELECT Q FROM orders WHERE customer_num=104 AND order_num>1001 UNION SELECT Q FROM orders WHERE order_num=1008 q样p利用索引路径处理查询。?br /> 8 避免相关子查询: 一个列的标{时在L询和WHERE子句中的查询中出玎ͼ那么很可能当L询中的列值改变之后,子查询必重新查询一ơ。查询嵌套层ơ越多,效率低Q因此应当尽量避免子查询。如果子查询不可避免Q那么要在子查询中过滤掉可能多的行。?br /> 9 避免困难的正规表辑ּQ?br />MATCHES和LIKE关键字支持通配W匹配,技术上叫正规表辑ּ。但q种匚w特别耗费旉。例如:SELECT Q FROM customer WHERE zipcode LIKE ?8_ _ _”?br />即在zipcode字段上徏立了索引Q在q种情况下也q是采用序扫描的方式。如果把语句改ؓSELECT Q FROM customer WHERE zipcode >?8000”,在执行查询时׃利用索引来查询,昄会大大提高速度。?br />另外Q还要避免非开始的子串。例如语句:SELECT Q FROM customer WHERE zipcode[2Q?] >?0”,在WHERE子句中采用了非开始子Ԍ因而这个语句也不会使用索引。?br /> 10 不充份的q接条gQ?br />例:表card?896行,在card_no上有一个非聚集索引Q表account?91122行,在account_no上有一个非聚集索引Q试看在不同的表q接条g下,两个SQL的执行情况: SELECT sum(a.amount) FROM account a,card b WHERE a.card_no = b.card_no Q?0U) SQL改ؓQ?br />SELECT sum(a.amount) FROM account a,card b WHERE a.card_no = b.card_no and a.account_no=b.account_no Q?lt; 1U) 分析Q?br />在第一个连接条件下Q最x询方案是account作外层表Qcard作内层表Q利用card上的索引Q其I/Oơ数可由以下公式估算为: 外层表account上的22541?Q外层表account?91122?内层表card上对应外层表W一行所要查扄3)=595907ơI/O 在第二个q接条g下,最x询方案是card作外层表Qaccount作内层表Q利用account上的索引Q其I/Oơ数可由以下公式估算为: 外层表card上的1944?Q外层表card?896?内层表account上对应外层表每一行所要查扄4)= 33528ơI/O 可见Q只有充份的q接条gQ真正的最x案才会被执行。?br />多表操作在被实际执行前,查询优化器会Ҏ(gu)q接条gQ列出几l可能的q接Ҏ(gu)q从中找出系l开销最的最x案。连接条件要充䆾考虑带有索引的表、行数多的表Q内外表的选择可由公式Q外层表中的匚w行数*内层表中每一ơ查扄ơ数定Q乘U最ؓ最x案。?br />不可优化的WHERE子句 ? 下列SQL条g语句中的列都建有恰当的烦引,但执行速度却非常慢Q?br />SELECT * FROM record WHERE substrINg(card_no,1,4)='5378' (13U? SELECT * FROM record WHERE amount/30< 1000 Q?1U) SELECT * FROM record WHERE convert(char(10),date,112)='19991201' Q?0U) 分析Q?br />WHERE子句中对列的M操作l果都是在SQLq行旉列计算得到的,因此它不得不q行表搜索,而没有用该列上面的索引Q如果这些结果在查询~译时就能得刎ͼ那么可以被SQL优化器优化,使用索引Q避免表搜烦Q因此将SQL重写成下面这P SELECT * FROM record WHERE card_no like '5378%' Q?lt; 1U) SELECT * FROM record WHERE amount< 1000*30 Q?lt; 1U) SELECT * FROM record WHERE date= '1999/12/01' Q?lt; 1U)
11 存储q程中,采用临时表优化查询: 例?br />1Q从parven表中按vendor_num的次序读数据Q?br />SELECT part_numQvendor_numQprice FROM parven ORDER BY vendor_num INTO temp pv_by_vn q个语句序读parvenQ?0)Q写一个时表Q?0)Qƈ排序。假定排序的开销?00,d?00c?br />2Q把临时表和vendor表连接,把结果输出到一个时表Qƈ按part_num排序Q?br />SELECT pv_by_vnQ* vendor.vendor_num FROM pv_by_vnQvendor WHERE pv_by_vn.vendor_num=vendor.vendor_num ORDER BY pv_by_vn.part_num INTO TMP pvvn_by_pn DROP TABLE pv_by_vn q 个查询dpv_by_vn(50?Q它通过索引存取vendor?.5万次Q但׃按vendor_numơ序排列Q实际上只是通过索引序地读 vendor表(40Q?=42)Q输出的表每늺95行,?60c写q存取这些页引发5Q?60=800ơ的dQ烦引共d892c?br />3Q把输出和partq接得到最后的l果Q?br />SELECT pvvn_by_pn.Q,part.part_desc FROM pvvn_by_pnQpart WHERE pvvn_by_pn.part_num=part.part_num DROP TABLE pvvn_by_pn q样Q查询顺序地读pvvn_by_pn(160?Q通过索引读part?.5万次Q由于徏有烦引,所以实际上q行1772ơ磁盘读写,优化比例?0?。?br /> 好了Q搞定。?br />其实sql的优化,各种数据库之间都是互通的?img src ="http://www.aygfsteel.com/liujw/aggbug/39143.html" width = "1" height = "1" />