1 尽量不要在where中包含子查询; 关于时间的查询,尽量不要写成:where to_char(dif_date,'yyyy-mm-dd')=to_char('2007-07-01','yyyy-mm-dd'); 2在过滤条件中,可以过滤掉最大数量记录的条件必须放在where子句的末尾; FROM子句中写在最后的表(基础表,driving table)将被最先处理,在FROM子句中包含多个表的情况下,你必须选择记录条数最少的表作为基础表。如果有三个以上的连接查询,那就需要选择交叉表(intersection table)作为基础表,交叉表是指那个被其他表所引用的表; 3采用绑定变量 4在WHERE中尽量不要使用OR 5用EXISTS替代IN、用NOT EXISTS替代NOT IN; 6避免在索引列上使用计算:WHERE SAL*12>25000; 7用IN来替代OR: WHERE LOC_ID=10 OR LOC_ID=15 OR LOC_ID=20 8避免在索引列上使用IS NULL和IS NOT NULL; 9总是使用索引的第一个列; 10用UNION-ALL替代UNION; 11避免改变索引列的类型:SELECT...FROM EMP WHERE EMPNO='123',由于隐式数据类型转换,to_char(EMPNO)='123',因此,将不采用索引,一般在采用字符串拼凑动态SQL语句出现; 12'!=' 将不使用索引; 13优化GROUP BY; 14避免带有LIKE参数的通配符,LIKE '4YE%'使用索引,但LIKE '%YE'不使用索引 15避免使用困难的正规表达式,例如select * from customer where zipcode like "98___",即便在zipcode上建立了索引,在这种情况下也还是采用顺序扫描的方式。如果把语句改成select * from customer where zipcode>"98000",在执行查询时就会利用索引来查询,显然会大大提高速度; 16尽量明确的完成SQL语句,尽量少让数据库工作。比如写SELECT语句时,需要把查询的字段明确指出表名。尽量不要使用SELECT *语句。组织SQL语句的时候,尽量按照数据库的习惯进行组织。
========================================================================
SQL与效率[z]
From: http://user.qzone.qq.com/67622728/blog/1198426480 ORACLE的解析器按照从右到左的顺序处理FROM子句中的表名,因此FROM子句中写在最后的表(基础表 driving table)将被最先处理. 在FROM子句中包含多个表的情况下,你必须选择记录条数最少的表作为基础表.当ORACLE处理多个表时, 会运用排序及合并的方式连接它们.首先,扫描第一个表(FROM子句中最后的那个表)并对记录进行派序,然后扫描第二个表(FROM子句中最后第二个表),最后将所有从第二个表中检索出的记录与第一个表中合适记录进行合并. 例如: A表2万条记录,B表1条记录 选择B作为基础表 (最好的方法) select count(*) from a ,b执行时间0.96秒 选择TAB2作为基础表 (不佳的方法) select count(*) from b,a 执行时间26.09秒 SELECT子句中避免使用 ‘ * ‘ 当你想在SELECT子句中列出所有的COLUMN时,使用动态SQL列引用 ‘*’ 是一个方便的方法.不幸的是,这是一个非常低效的方法. 实际上,ORACLE在解析的过程中, 会将’*’ 依次转换成所有的列名, 这个工作是通过查询数据字典完成的, 这意味着将耗费更多的时间. <> 操作符(不等于) 不等于操作符是永远不会用到索引的,因此对它的处理只会产生全表扫描。 推荐方案:用其它相同功能的操作运算代替,如 a<>0 改为 a>0 or a<0 a<>’’ 改为 a>’’ WHERE子句中的连接顺序. ORACLE采用自下而上的顺序解析WHERE子句,根据这个原理,表之间的连接必须写在其他WHERE条件之前, 那些可以过滤掉最大数量记录的条件必须写在WHERE子句的末尾. 例如:(低效,执行时间156.3秒) select * from emp e where sal > 50000 and job = ‘manager’ and 25 < (select count(*) from emp where mgr=e.empno); (高效,执行时间10.6秒) select * from emp e where 25 < (select count(*) from emp where mgr=e.empno) and sal > 50000 and job = ‘manager’; LIKE操作符 LIKE操作符可以应用通配符查询,里面的通配符组合可能达到几乎是任意的查询,但是如果用得不好则会产生性能上的问题,如LIKE ‘T00%’ 这种查询不会引用索引,而LIKE ‘X5400%’则会引用范围索引。一个实际例子:用YW_YHJBQK表中营业编号后面的户标识号可来查询营业编号 YY_BH LIKE ‘T00%’ 这个条件会产生全表扫描,如果改成YY_BH LIKE ’X5400%’ OR YY_BH LIKE ’B5400%’ 则会利用YY_BH的索引进行两个范围的查询,性能肯定大大提高。 尽量多使用COMMIT 只要有可能,在程序中尽量多使用COMMIT, 这样程序的性能得到提高,需求也会因为COMMIT所释放的资源而减少: COMMIT所释放的资源: a. 回滚段上用于恢复数据的信息. b. 被程序语句获得的锁 c. redo log buffer 中的空间 d. ORACLE为管理上述3种资源中的内部花费 减少对表的查询,在含有子查询的SQL语句中,要特别注意减少对表的查询. 尽量多使用表的别名(Alias),当在SQL语句中连接多个表时, 请使用表的别名并把别名前缀于每个Column上.这样一来,就可以减少解析的时间并减少那些由Column歧义引起的语法错误. IN 操作符 用IN写出来的SQL的优点是比较容易写及清晰易懂,这比较适合现代软件开发的风格。 但是用IN的SQL性能总是比较低的,从ORACLE执行的步骤来分析用IN的SQL与不用IN的SQL有以下区别: ORACLE试图将其转换成多个表的连接,如果转换不成功则先执行IN里面的子查询,再查询外层的表记录,如果转换成功则直接采用多个表的连接方式查询。由此可见用IN的SQL至少多了一个转换的过程。一般的SQL都可以转换成功,但对于含有分组统计等方面的SQL就不能转换了。 推荐方案:在业务密集的SQL当中尽量不采用IN操作符。 NOT IN操作符 此操作是强列推荐不使用的,因为它不能应用表的索引。 推荐方案:用NOT EXISTS 或(外连接+判断为空)方案代替。=====================================================================
SQL执行效率
SQL语句中,IN、EXISTS、NOT IN、NOT EXISTS的效率较低,尤其是后两种语句,当数据量较大时,更常给人一种死机般的感觉。本文提供一种使用连接的方法代替以上的四种语句,可大副提高SQL语句的运行效率。以NOT IN为例,当数据量达到一万时,效率可提高20倍,数据量越大,效率提高的幅度也就越大。本文所举的例子在Oracle 7.0下运行通过,但本文所推荐的方法在各种大型数据库上皆适用。为了能够更好的说明问题,我们采用示例的方式来说明问题。下面,我们将创建一些数据库表和数据,用于在举例时使用。下面的语句将创建我们示例中将使用的表,并分别在表1(TAB1)中存入10000条数据,表2(TAB2)中存入5000条数据。SQL语句如下:
CREATE TABLE TAB1(COL1 VARCHAR(20) NOT NULL,COL2 INTEGER,PRIMARY KEY(COL1));CREATE TABLE TAB2(COL1 VARCHAR(20) NOT NULL,PRIMARY KEY(COL1));CREATE TABLE TAB3(COL1 VARCHAR(20) NOT NULL,PRIMARY KEY(COL1));CREATE OR REPLACE TRIGGER T_TAB3 BEFORE INSERT ON TAB3 FOR EACH ROW DECLARE NUM1 NUMBER;BEGINNUM1:=1;LOOPEXIT WHEN NUM1>10000;INSERT INTO TAB1 VALUES (NUM1,NUM1);IF NUM1<=5000 THEN INSERT INTO TAB2 VALUES (NUM1);END IF;NUM1:=NUM1+1;END LOOP;END;INSERT INTO TAB3 VALUES('1');
下面,我们将举2个例子来具体说明使用连接替换IN、NOT IN、EXISTS、NOT EXISTS的方法。 读取表1中第2列(COL2)数据的总和,且其第1列数据存在于表2的第1列中。1. 使用IN的SQL语句:SELECT SUM(COL2) FROM TAB1 WHERE COL1 IN(SELECT COL1 FROM TAB2)2. 使用EXISTS的SQL语句:SELECT SUM(COL2) FROM TAB1 WHERE EXISTS(SELECT * FROM TAB2 WHERE TAB1.COL1=TAB2.COL1)3. 使用连接的SQL语句:SELECT SUM(A.COL2) FROM TAB1 A,TAB2 B WHERE A.COL1=B.COL1 读取表1中第2列(COL2)数据的总和,且其第1列数据不存在于表2的第1列中。1. 使用NOT IN的SQL语句:SELECT SUM(COL2) FROM TAB1 WHERE COL1 NOT IN(SELECT COL1 FROM TAB2)2. 使用NOT EXISTS的SQL语句:SELECT SUM(COL2) FROM TAB1 WHERE NOT EXISTS(SELECT * FROM TAB2 WHERE TAB1.COL1=TAB2.COL1)3. 使用外连接的SQL语句:SELECT SUM(A.COL2) FROM TAB1 A,TAB2 B WHERE A.COL1=B.COL1(+) AND B.COL1 IS NULL
下面介绍IN、NOT IN、EXIST、NOT EXIST在DELETE和UPDATE语句中的效率提高方法。下面所举的例子在Microsoft SQL Server 7.0下运行通过,但所推荐的方法在各种大型数据库上皆适用。下面,我们将创建一些数据库表和数据,用于举例说明。我们将分别在表A(TA)中存入 10000条数据,表B(TB)中存入5000条数据。 SQL语句如下:
CREATE TABLE TA(CA INT)CREATE TABLE TB(CA INT)CREATE TABLE TC(CA INT)CREATE TRIGGER TRA ON TC FOR INSERT ASDECLARE @MINT INTBEGINSELECT @MINT=1WHILE (@MINT<=5000)BEGININSERT INTO TA VALUES(@MINT)INSERT INTO TB VALUES(@MINT)SELECT @MINT=@MINT+1ENDWHILE (@MINT<=10000)BEGININSERT INTO TA VALUES(@MINT)SELECT @MINT=@MINT+1ENDENDGOINSERT INTO TC VALUES(1)GO
删除表A中表A和表B相同的数据1. 用IN的SQL语句:DELETE FROM TA WHERE TA.CA IN (SELECT CA FROM TB)2. 用EXISTS的SQL语句:DELETE FROM TA WHERE EXISTS (SELECT * FROM TB WHERE TB.CA=TA.CA)3. 使用连接的SQL语句:DELETE TA FROM TA,TB WHERE TA.CA=TB.CA 删除表A中表A存在但表B中不存在的数据1. 使用IN的SQL语句:DELETE FROM TA WHERE TA.CA NOT IN (SELECT CA FROM TB)2. 使用EXISTS的SQL语句:DELETE FROM TA WHERE NOT EXISTS (SELECT CA FROM TB WHERE TB.CA=TA.CA)3. 使用连接的SQL语句:DELETE TA FROM TA LEFT OUTER JOIN TB ON TA.CA=TB.CA WHERE TB.CA IS NULL 更新表A中表A和表B相同的数据1. 使用IN的SQL语句:UPDATE TA SET CA=CA+10000 WHERE CA IN (SELECT CA FROM TB)2. 使用EXISTS的SQL语句:UPDATE TA SET CA=CA+10000 WHERE EXISTS (SELECT CA FROM TB WHERE TB.CA=TA.CA)3. 使用连接的SQL语句:UPDATE TA SET TA.CA=TA.CA+10000 FROM TA,TB WHERE TA.CA=TB.CA 更新表A中表A存在但表B中不存在的数据1. 使用IN的SQL语句:UPDATE TA SET CA=CA+10000 WHERE CA NOT IN (SELECT CA FROM TB)2. 使用EXISTS的SQL语句:UPDATE TA SET CA=CA+10000 WHERE NOT EXISTS (SELECT CA FROM TB WHERE TB.CA=TA.CA)3. 使用连接的SQL语句:UPDATE TA SET TA.CA=TA.CA+10000 FROM TA LEFT OUTER JOIN TB ON TA.CA=TB.CA WHERE TB.CA IS NULL