贵宾厅(中国区)官方网站

    贵宾厅(中国区)官方网站

    SQL语句优化(huà)提高(gāo)数据库性能

    发(fā)布于: 2016-06-06    浏览: 10511    作(zuò)者:王佳林

    为了获(huò)得稳定(dìng)的执行性能,SQL语句越简(jiǎn)单(dān)越好。对复杂的SQL语句,要设法对之进行简化(huà),本文给大家介绍优化SQL语句提高(gāo)数据库性能。


    现在(zài)数据越(yuè)来越(yuè)复杂和庞(páng)大,很多时候影响程序运行性能不(bú)理想的原因中(zhōng)除(chú)了一部分是因为应用程(chéng)序的负载确实超过了(le)服务器的实际处理能力(lì)外,更多的是因(yīn)为系统存在大量的SQL语(yǔ)句(jù)需要优(yōu)化(huà)。

    一、问题的提出

    在项目实际使用中,数据是一个(gè)长期累计的过程,随着数据(jù)库中(zhōng)数据的增(zēng)加,系统的响应速度就成为目前系(xì)统需要解(jiě)决的最主要的问题之一。系统优化中一(yī)个很重要的方面就(jiù)是SQL语句的优化。对于海量数据(jù),劣质SQL语句和优质(zhì)SQL语句之间的速度(dù)差别可以达到(dào)成(chéng)千(qiān)上百倍,因此高质量的SQL语(yǔ)句,更能提高系统的可用性(xìng)。

    二、SQL语(yǔ)句编写注意问题(tí)

    下面就某些SQL语句(jù)的where子句编写中(zhōng)需(xū)要注(zhù)意的问(wèn)题作详细介绍。在这些(xiē)where子句(jù)中,即使(shǐ)某些(xiē)列存在索引(yǐn),但(dàn)是由于(yú)编写了(le)劣质的SQL,系统在(zài)运行该SQL语句时(shí)也不能使(shǐ)用该索引,而同样使用全表扫描,这就造成了响应速度的(de)极(jí)大降低(dī)。

    1. 操作符优化

    (a) IN 操作符(fú)

     在使用中尽量用EXISTS替(tì)代IN、用(yòng)NOT EXISTS替(tì)代NOT IN 

    在(zài)许多基于(yú)基础表的查询中,为了(le)满(mǎn)足一个条件,往往需要对另一个表进行联接。在这种情况(kuàng)下, 使用EXISTS(NOT EXISTS)通(tōng)常(cháng)将提高查询的效率。。在子查询中,NOT IN子句将执行一个内部的排序(xù)和(hé)合并。 无论在哪种(zhǒng)情况下,NOT IN都是(shì)最低效(xiào)的 (因为它对子(zǐ)查询中的表执(zhí)行了一个全(quán)表(biǎo)遍(biàn)历(lì))。。为了避免(miǎn)使用NOT IN ,我们(men)可以把它(tā)改写(xiě)成外(wài)连接(jiē)(Outer Joins)NOT EXISTS

    例子: 
    (推荐(jiàn))select* from dt_article where exists(select id from dt_article_category wheredt_article_category。id=dt_article。category_id andtitle='公司新闻(wén)')
    (不推荐)select* from dt_article where category_id in (select id from dt_article_categorywhere title='公(gōng)司新闻')

     

    (b) IS NULL IS NOT NULL操作(判断字(zì)段是否为空)

    判断字段是否为空(kōng)一(yī)般是不(bú)会应(yīng)用索引的,因为索引(yǐn)是不索引空(kōng)值(zhí)的。不能用null作索引,任何(hé)包含null值的列(liè)都将不(bú)会(huì)被包含(hán)在索引(yǐn)中。即使索(suǒ)引有多列这样的情况下,只要这些列中有一列含有null,该列就会从索引中排(pái)除。也(yě)就(jiù)是说如果某列存(cún)在空值,即使对该列建索引也不会提高性能。任何在where子句中使用is null或is not null的(de)语句优(yōu)化器是不允许使用(yòng)索引的。 

        例(lì)子:

    (推荐)select* from dt_article where title>'';
     (不推荐)select* from dt_article where title is null;

    (c) > < 操作符(大于或小(xiǎo)于操作符)

    (推荐)select * from dt_article where id>=101;

    (不推荐)select * from dt_article where id>100;

    两者的区(qū)别在(zài)于, 前者将直(zhí)接跳到(dào)第(dì)一(yī)个id等于101的记录而(ér)后者(zhě)将(jiāng)首先定位到id=100的记录并且向前扫(sǎo)描到(dào)第一个id大于100的记(jì)录(lù)。

    (d)LIKE操作符

    LIKE操(cāo)作符可以应(yīng)用通配符查询,里面(miàn)的通配符组合可(kě)能达到几乎是任意的查询,但是如果用得不好则会产生性能上的问题(tí),如like '%福(fú)瑞希%'这种查询不会引(yǐn)用索引(yǐn),而like'福瑞希%'则(zé)会(huì)引用范围索(suǒ)引。

    一个实(shí)际例子:用(yòng)dt_article表中内容可来查询, content like'%福瑞希%'这个条件会产生全(quán)表扫描,如果改成contentlike '福瑞(ruì)希%'则(zé)会利用content的索引进行范围(wéi)的查询,性能(néng)肯定大大(dà)提高。

    在很多情(qíng)况(kuàng)下可能无法避免这种情况(kuàng),但是一定要心中有底(dǐ),通配符如此使用会(huì)降低(dī)查询(xún)速度。然而当通配符出现(xiàn)在字(zì)符串其他位置时,优化(huà)器就能利用索引。

    (e) UNION操作符

    当SQL语句需要UNION两个(gè)查询结果(guǒ)集(jí)合时,这(zhè)两(liǎng)个结(jié)果集合会以(yǐ)UNION-ALL的方式被合并, 然后在输(shū)出(chū)最(zuì)终结果前进行(háng)去重和排序。 假如用UNION ALL替代UNION, 这样(yàng)排序就不是必要了。 效率就会因此得到提高。 需要注重的是(shì),UNION ALL 将重(chóng)复输出两个(gè)结果集合中相同记录。 因此各位还是要从(cóng)业务需求分析使(shǐ)用(yòng)UNIONALL的可行性。 UNION 将对结果集合去重排序,这个(gè)操作会使(shǐ)用到(dào)SORT_AREA_SIZE这块(kuài)内存。 对于(yú)这块内(nèi)存的优化(huà)也是(shì)相当重要的。

    (f) NOT

    我们(men)要避(bì)免在索引(yǐn)列(liè)上使用NOT, NOT会产生(shēng)在和在索引列上使用函(hán)数相同的影响。 当查询列碰到”NOT,他就会停(tíng)止使用索引转而执行全表(biǎo)扫描

    (g) OR

        通常情况下, 用UNION替换WHERE子句中(zhōng)的OR将会(huì)起到较好的效果。 对索引列(liè)使用OR将(jiāng)造成全表扫描(miáo)。 注重(chóng), 以上规则只针(zhēn)对多个索引列有效。 假如有column没(méi)有被索(suǒ)引, 查询效率(lǜ)可能会因为(wéi)你(nǐ)没有选(xuǎn)择OR而(ér)降低。 在下面(miàn)的例(lì)子中, title和(hé)category_id上都建有(yǒu)索引。

    (推荐)select * from dt_article where title='清洗(xǐ)空(kōng)气' union all select * from dt_article where category_id=92

    (不推荐)select * from dt_article where title='清洗空气(qì)' or category_id=92 假如你坚持要用OR, 那就需(xū)要返(fǎn)回记录最少(shǎo)的索引列写在最前面。 
           另外(wài)在一些情况下,也可以使用IN来替代OR,     这(zhè)是一条简单易(yì)记(jì)的规则,但(dàn)是实际的执行(háng)效果还须检验。

    (推荐)select * from dt_article where category_id in (89,92)

    (不推荐)select * from dt_article where category_id=92 or category_id=89

    (h) DISTINCT

         当提交一个包含一对多(duō)表信息的(de)查询时,避免在SELECT子(zǐ)句中使用DISTINCT。 一般可以考虑用EXIST替换, EXISTS 使查询更(gèng)为迅速,因为RDBMS核(hé)心模块将在子查询的条件一(yī)旦满(mǎn)足后,马上返回结果。 

    2. SQL书(shū)写的(de)影响

     (a) WHERE后面的条(tiáo)件顺序影响(xiǎng)

    WHERE子句(jù)后(hòu)面的(de)条件顺序对大数据量表的(de)查询会产生直接(jiē)的影响。如:

    select * from dt_article where category_id=92 and is_hot=1
    select * from dt_article where is_hot=1 and category_id=92 

    以上两个SQL中(zhōng)category_id(电压等级)及is_hot(销户(hù)标志)两个(gè)字段都(dōu)没(méi)进行索引(yǐn),所以执行(háng)的时(shí)候都是全表扫描,第(dì)一条SQL的is_hot=1在记录集内比率(lǜ)为(wéi)99%,而category_id=92的比率只为1%,在进行第一条SQL的时候99%条记录都进(jìn)行category_id及is_hot的比(bǐ)较,而(ér)在进(jìn)行第二(èr)条SQL的时候1%条记录(lù)都进行category_id及is_hot的比较,以此可以得出第二条(tiáo)SQL的CPU占用率明显比第(dì)一条低(dī)。

    WHERE解析(xī)是采用自下而上的顺(shùn)序解析(xī)WHERE子句,根据这(zhè)个(gè)原理,表之间的连接必须(xū)写在其他WHERE条(tiáo)件之前, 那些可以(yǐ)过滤(lǜ)掉最大(dà)数量记(jì)录的条件(jiàn)必须(xū)写(xiě)在WHERE子(zǐ)句的末尾。 

    3. 更多(duō)方(fāng)面SQL优化资料分(fèn)享(xiǎng)

    (1) 选择最有效率的(de)表(biǎo)名顺序(只在基于规则的优化器中(zhōng)有效):

    ORACLE 的解析器(qì)按照从右到左的顺序处理FROM子(zǐ)句中的表名,FROM子句中写在(zài)最(zuì)后的表(基础表 driving table)将(jiāng)被最先处理,在FROM子句中包含多个表的情况下(xià),你(nǐ)必须选择记录条数最少的表作为基础表。如(rú)果(guǒ)有(yǒu)3个以上的表连接(jiē)查询(xún), 那就需要选择交叉表(intersectiontable)作为(wéi)基础表, 交叉表(biǎo)是指(zhǐ)那个被其他(tā)表所引用的表.

    (2) SELECT子句中避免使用 ‘ * ‘:

    ORACLE在(zài)解析(xī)的过程中, 会(huì)将'*' 依次(cì)转(zhuǎn)换成所有(yǒu)的(de)列名, 这个工(gōng)作是通过查询数据字典完成的, 这意味着将耗费更多的时间。

    (3) 减少访(fǎng)问数据库的次数:

    ORACLE在(zài)内部执行了许多工作: 解(jiě)析SQL语句, 估算索引的利用率, 绑定变量 , 读数据(jù)块等。

    (4) 整合简单(dān),无关(guān)联的数据库(kù)访问(wèn):

    如果你有几(jǐ)个(gè)简单的数据(jù)库查(chá)询语句,你可以把它(tā)们整合到一个查询中(zhōng)(即使它们之间没有关系) 。

    (5) 用(yòng)TRUNCATE替代DELETE:

    当(dāng)删除表中的记录时,在(zài)通常情况下(xià), 回滚(gǔn)段(rollbacksegments ) 用来(lái)存放(fàng)可以被恢复(fù)的信息(xī). 如果你没有COMMIT事务,ORACLE会将数据(jù)恢复到(dào)删除之前的状态(tài)(准确地(dì)说是恢复到执行删除命令之前的状况) 而当运用TRUNCATE时, 回滚段不再存放任何可被恢(huī)复的信息.当命令运(yùn)行后(hòu),数据不能被(bèi)恢复.因此很(hěn)少的资源被调用,执(zhí)行时间也会很短. (译者按: TRUNCATE只在删除全(quán)表适用,TRUNCATE是DDL不是(shì)DML) 。

    (6) 尽量(liàng)多使用(yòng)COMMIT:

    只要有可能,在(zài)程序中尽量多使(shǐ)用(yòng)COMMIT, 这样程序的性(xìng)能得到提高,需(xū)求也会因(yīn)为COMMIT所释放的(de)资源而(ér)减少,COMMIT所释放的资(zī)源:

    a. 回(huí)滚(gǔn)段(duàn)上用于恢(huī)复数据的信息.
    b. 被程(chéng)序语句获(huò)得(dé)的锁
    c. redo log buffer 中的空间

    (7) 通过(guò)内部函数提(tí)高SQL效(xiào)率(lǜ):

    复杂的SQL往往牺牲了执行效率. 能够掌(zhǎng)握上(shàng)面的运用函数解决问题的方法(fǎ)在实际工作中是(shì)非常有意义的。

    (8) 使(shǐ)用表的(de)别名(Alias):

    当在SQL语句中连(lián)接(jiē)多个表时, 请使用(yòng)表(biǎo)的别(bié)名并把别名前缀于每个(gè)Column上(shàng).这样一来,就(jiù)可以减少解(jiě)析的(de)时间并减少那些由Column歧义引起(qǐ)的语法错(cuò)误。

    (9) 总是使用索引(yǐn)的(de)第一个(gè)列:

    如(rú)果索引是(shì)建立在多个列上, 只有在它(tā)的第一个列(leading column)被where子句引用时(shí),优化器(qì)才会选择使(shǐ)用该索引. 这(zhè)也是(shì)一条简单而(ér)重要的规则(zé),当仅(jǐn)引用(yòng)索引的第二个(gè)列时,优化器使(shǐ)用了全表扫描而忽略了索引。

    (10) 避免使用耗(hào)费资源的操作:

    带(dài)有(yǒu)DISTINCT,UNION,MINUS,INTERSECT,ORDER BY的SQL语句(jù)会启动SQL引(yǐn)擎(qíng)执(zhí)行耗费资(zī)源的排序(xù)(SORT)功(gōng)能. DISTINCT需(xū)要一次排(pái)序操作, 而其他的至少需要(yào)执行两次排序. 通常, 带有UNION, MINUS , INTERSECT的(de)SQL语句都可以用其他方式重写. 如果你的数据库的SORT_AREA_SIZE调配得好, 使用UNION , MINUS, INTERSECT也是可以(yǐ)考虑的, 毕竟它(tā)们(men)的可读性很(hěn)强。

    在线(xiàn)客服

    售前(qián)咨询(xún)

    售(shòu)后服务

    投诉/建议

    服务(wù)热线
    0731-83091505
    18874148081

    贵宾厅(中国区)官方网站

    贵宾厅(中国区)官方网站

    相关信息

    "贵宾厅 沈阳贵宾厅进出口贸易有限公司沈阳贵宾厅进出口贸易有限公司成立于2016年,总部位于辽宁沈阳。公司致力于为生命科学领域研究者提供专业的科研工具及服务。公司拥有专业的售前、售后团队,建立以实验室设备销售为主体,集试剂、耗材销售、技术推广、售后服务、物流仓储为一体的综合型服务平台。

    更新时间:2025-07-17 19:57 来源:13020170768.en.linyi.liaocheng.jixi.ww38.viennacitytours.com