zoukankan      html  css  js  c++  java
  • 优化更新语句中的标量子查询

    数据库环境:SQL SERVER 2008R2

    今天看到开发写的一条更新语句,第一眼是觉得这个SQL的业务有问题,再细看子查询部分,才意识到这是开发人员使的“怪招”。

    这个SQL能满足业务的需要,只是开发人员在写这个SQL的时候应该不会考虑到存在性能问题。具体SQL如下:

    UPDATE  fapply_04
    SET     conf_y_fee_amt = ISNULL(conf_y_fee_amt, 0)
            + ISNULL(( SELECT   SUM(fexp_03.opr_amt)
                       FROM     fexp_03
                       WHERE    fexp_03.com_id = fapply_04.com_id
                                AND fexp_03.origin_no = fapply_04.fapply_no
                                AND fexp_03.origin_line_no = fapply_04.line_no
                                AND fexp_03.feetype_flag = ''
                                AND fexp_03.fexp_no = :fexp_no
                     ), 0)

    开发人员原本想把fexp_03表中的opr_amt累加到对应记录的fapply_04表的conf_y_fee_amt字段上,但是,这条SQL实际上

    把fapply_04整张表都更新了(没有对应记录则累加0)。

    现在我们来做个实验,验证一下我的说法

    1.数据准备

    创建2张表,分别是表a和表b,脚本脚本如下:

    --创建a表
    SELECT * INTO a FROM 
    (
    SELECT 1 AS id,10 AS score
    UNION ALL
    SELECT 2 AS id,20 AS score
    UNION ALL
    SELECT 3 AS id,30 AS score
    UNION ALL
    SELECT 4 AS id,40 AS score) t
    --创建b表
    SELECT * INTO b FROM(
    SELECT 2 AS id,-20 AS cn) t

    2.更新数据

    如果a表和b表的id匹配,则累加b表对应的cn字段的数据,否则,减5。

    UPDATE  a
    SET     score = score + ISNULL(( SELECT cn
                                     FROM   b
                                     WHERE  b.id = a.id
                                   ), -5)
    SELECT  *
    FROM    a

    好,我们现在来对比一下更新前后,a表数据的变化。左图是更新前,右图是更新后。

         

    看到没,是不是a表发生了全表更新呢?

    全表更新把不必要的记录也更新了,数据量大的时候,会造成不必要的影响。

    那怎么避免这个问题呢?

    把子查询改成内连接即可,看脚本

    UPDATE  a
    SET     a.score = a.score + b.cn
    FROM    a
            INNER JOIN b ON a.id = b.id
    SELECT  *
    FROM    a

    再贴上结果图,对比前面的子查询更新的图,是不是用内连接只更新了条件匹配的记录?

    现在我们回到开始的案例,我把更新语句改成查询的了,然后执行。总共查询出476056条记录,耗时30s。

    SELECT  conf_y_fee_amt = ISNULL(conf_y_fee_amt, 0)
            + ISNULL(( SELECT   SUM(fexp_03.opr_amt)
                       FROM     fexp_03
                       WHERE    fexp_03.com_id = fapply_04.com_id
                                AND fexp_03.origin_no = fapply_04.fapply_no
                                AND fexp_03.origin_line_no = fapply_04.line_no
                                AND fexp_03.feetype_flag = ''
                                AND fexp_03.fexp_no = '200710000335'
                     ), 0)
    FROM    fapply_04
    /*分别统计fapply_04,fexp_03的数据*/
    SELECT  COUNT(*)
    FROM    fexp_03
    WHERE   fexp_03.feetype_flag = ''
            AND fexp_03.fexp_no = '200710000335'--3
    SELECT  COUNT(*)
    FROM    fapply_04--476056

    然后,把SQL改写成内联接的方式,我们再来看一下执行情况。查询1条数据,耗时0s。

    SELECT  conf_y_fee_amt = ISNULL(fapply_04.conf_y_fee_amt, 0)
            + ISNULL(fexp_03.opr_amt, 0)
    FROM    fapply_04
            INNER JOIN ( SELECT com_id ,
                                origin_no ,
                                origin_line_no ,
                                SUM(fexp_03.opr_amt) opr_amt
                         FROM   fexp_03
                         WHERE  fexp_03.feetype_flag = ''
                                AND fexp_03.fexp_no = '200710000335'
                         GROUP BY com_id ,
                                origin_no ,
                                origin_line_no
                       ) fexp_03 ON fexp_03.com_id = fapply_04.com_id
                                    AND fexp_03.origin_no = fapply_04.fapply_no
                                    AND fexp_03.origin_line_no = fapply_04.line_no
    /*分别统计fapply_04,fexp_03的数据*/
    SELECT  COUNT(*)
    FROM    fexp_03
    WHERE   fexp_03.feetype_flag = ''
            AND fexp_03.fexp_no = '200710000335'--3
    
    SELECT  COUNT(*)
    FROM    fapply_04--476056

    综上,根据业务的意思,原本只需更新一条记录的,由于使用子查询,做了全表更新。

    小结一下,有些时候,我们光是把SQL实现了还不够,还要考虑我们的代码会不会造成性能问题,再多想想可能的实现方法。

    (本文完)

  • 相关阅读:
    《.NET内存管理宝典 》(Pro .NET Memory Management) 阅读指南
    《.NET内存管理宝典 》(Pro .NET Memory Management) 阅读指南
    《.NET内存管理宝典 》(Pro .NET Memory Management) 阅读指南
    使用Jasmine和karma对传统js进行单元测试
    《.NET内存管理宝典 》(Pro .NET Memory Management) 阅读指南
    《.NET内存管理宝典 》(Pro .NET Memory Management) 阅读指南
    nginx 基于IP的多虚拟主机配置
    Shiro 框架的MD5加密算法实现原理
    项目实战:Qt+OSG三维点云引擎(支持原点,缩放,单独轴或者组合多轴拽拖旋转,支持导入点云文件)
    实用技巧:阿里云服务器建立公网物联网服务器(解决阿里云服务器端口,公网连接不上的问题)
  • 原文地址:https://www.cnblogs.com/boss-he/p/4547425.html
Copyright © 2011-2022 走看看