一个教学系统的SQL优化

今天一个同学给我教学系统 sql 优化 ,  未优化之前用了1.2 秒出结果, 15000转硬盘

表结构:

CREATE TABLE `t_s_students` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `CLASSCODE` varchar(32) DEFAULT NULL,
  `STUDENTCODE` varchar(32) DEFAULT NULL,
  `STUDENTNAME` varchar(20) DEFAULT NULL,
  `CARDTYPE` varchar(20) DEFAULT NULL,
  `CARDNUMBER` varchar(30) DEFAULT NULL,
  `SEX` varchar(5) DEFAULT NULL,
  `AGE` varchar(5) DEFAULT NULL,
  `ZENGYUANCODE` varchar(20) DEFAULT NULL,
  `ZENGYUANNAME` varchar(20) DEFAULT NULL,
  `TRAINPASS` varchar(5) DEFAULT '0',
  `INCOMPANY` varchar(1) DEFAULT '0',
  `method` varchar(1) DEFAULT NULL,
  `agentcom` varchar(8) DEFAULT NULL,
  `repeatcount` varchar(10) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_tss_classcode` (`CLASSCODE`),
  KEY `idx_repeatcount_agentcom_CARDNUMBER_CLASSCODE` (`repeatcount`,`agentcom`,`CARDNUMBER`,`CLASSCODE`),
  KEY `idx_repeatcount_agentcom_CARDNUMBER_agentcom` (`agentcom`),
  KEY `idx_agentcom_CARDNUMBER_CLASSCODE` (`agentcom`,`CARDNUMBER`,`CLASSCODE`)
) ENGINE=InnoDB AUTO_INCREMENT=276898 DEFAULT CHARSET=utf8;

CREATE TABLE `t_s_students` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `CLASSCODE` varchar(32) DEFAULT NULL,
  `STUDENTCODE` varchar(32) DEFAULT NULL,
  `STUDENTNAME` varchar(20) DEFAULT NULL,
  `CARDTYPE` varchar(20) DEFAULT NULL,
  `CARDNUMBER` varchar(30) DEFAULT NULL,
  `SEX` varchar(5) DEFAULT NULL,
  `AGE` varchar(5) DEFAULT NULL,
  `ZENGYUANCODE` varchar(20) DEFAULT NULL,
  `ZENGYUANNAME` varchar(20) DEFAULT NULL,
  `TRAINPASS` varchar(5) DEFAULT '0',
  `INCOMPANY` varchar(1) DEFAULT '0',
  `method` varchar(1) DEFAULT NULL,
  `agentcom` varchar(8) DEFAULT NULL,
  `repeatcount` varchar(10) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_tss_classcode` (`CLASSCODE`),
  KEY `idx_repeatcount_agentcom_CARDNUMBER_CLASSCODE` (`repeatcount`,`agentcom`,`CARDNUMBER`,`CLASSCODE`),
  KEY `idx_repeatcount_agentcom_CARDNUMBER_agentcom` (`agentcom`),
  KEY `idx_agentcom_CARDNUMBER_CLASSCODE` (`agentcom`,`CARDNUMBER`,`CLASSCODE`)
) ENGINE=InnoDB AUTO_INCREMENT=276898 DEFAULT CHARSET=utf8;
SQL语句如下:

SELECT
d.noRepeatStudentNumber, d.noRepeatTrainPassNumber, concat(round(100 * ifnull(d.noRepeatTrainPassNumber / d.noRepeatStudentNumber, 0), 2), '%') AS noRepeatTrainPassRate, d.resultStudentNumber, d.trainPassNumber , concat(round(100 * ifnull(d.trainPassNumber / d.resultStudentNumber, 0), 2), '%') AS trainPassRate, d.incompanyNumber, concat(round(100 * ifnull(d.incompanyNumber / d.trainPassNumber, 0), 2), '%') AS incompanyRate FROM (SELECT ( SELECT COUNT(DISTINCT s1.cardnumber) FROM t_s_classes c, t_s_students s1 WHERE c.classcode = s1.classcode AND s1.repeatcount = '0' AND s1.agentcom LIKE concat('8007', '%') ) AS noRepeatStudentNumber, (SELECT COUNT(DISTINCT s2.cardnumber) FROM t_s_classes c, t_s_students s2 WHERE c.classcode = s2.classcode AND s2.trainpass = '1' AND c.classstatus = '2' AND s2.repeatcount = '0' AND s2.agentcom LIKE concat('8007', '%') ) AS noRepeatTrainPassNumber, (SELECT COUNT(DISTINCT s3.cardnumber) FROM t_s_classes c, t_s_students s3 WHERE c.classcode = s3.classcode AND s3.agentcom LIKE concat('8007', '%') ) AS resultStudentNumber, (SELECT COUNT(DISTINCT s4.cardnumber) FROM t_s_classes c, t_s_students s4 WHERE c.classcode = s4.classcode AND s4.trainpass = '1' AND c.classstatus = '2' AND s4.agentcom LIKE concat('8007', '%') ) AS trainPassNumber, (SELECT COUNT(DISTINCT s5.cardnumber) FROM t_s_classes c, t_s_students s5 WHERE c.classcode = s5.classcode AND s5.trainpass = '1' AND c.classstatus = '2' AND s5.incompany = '1' AND s5.agentcom LIKE concat('8007', '%') ) AS incompanyNumber FROM DUAL ) d;
执行计划:

+--------------+-----------------------+-----------------+----------------------+----------------+------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------+-------------------+-----------------------+----------------+--------------------+------------------------------------+
| id           | select_type           | table           | partitions           | type           | possible_keys                                                                                                                                  | key                                           | key_len           | ref                   | rows           | filtered           | Extra                              |
+--------------+-----------------------+-----------------+----------------------+----------------+------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------+-------------------+-----------------------+----------------+--------------------+------------------------------------+
| 1            | PRIMARY               | <derived2>      |                      | system         |                                                                                                                                                |                                               |                   |                       | 1              |                100 |                                    |
| 2            | DERIVED               |                 |                      |                |                                                                                                                                                |                                               |                   |                       |                |                    | No tables used                     |
| 7            | SUBQUERY              | c               |                      | ALL            | PRIMARY                                                                                                                                        |                                               |                   |                       | 3821           |                 10 | Using where                        |
| 7            | SUBQUERY              | s5              |                      | ref            | idx_tss_classcode,idx_repeatcount_agentcom_CARDNUMBER_agentcom,idx_agentcom_CARDNUMBER_CLASSCODE                                               | idx_tss_classcode                             | 99                | pro_oedu.c.CLASSCODE  | 86             |               0.42 | Using where                        |
| 6            | SUBQUERY              | c               |                      | ALL            | PRIMARY                                                                                                                                        |                                               |                   |                       | 3821           |                 10 | Using where                        |
| 6            | SUBQUERY              | s4              |                      | ref            | idx_tss_classcode,idx_repeatcount_agentcom_CARDNUMBER_agentcom,idx_agentcom_CARDNUMBER_CLASSCODE                                               | idx_tss_classcode                             | 99                | pro_oedu.c.CLASSCODE  | 86             |               4.23 | Using where                        |
| 5            | SUBQUERY              | s3              |                      | range          | idx_tss_classcode,idx_repeatcount_agentcom_CARDNUMBER_agentcom,idx_agentcom_CARDNUMBER_CLASSCODE                                               | idx_agentcom_CARDNUMBER_CLASSCODE             | 27                |                       | 90044          |                100 | Using where; Using index           |
| 5            | SUBQUERY              | c               |                      | eq_ref         | PRIMARY                                                                                                                                        | PRIMARY                                       | 98                | pro_oedu.s3.CLASSCODE | 1              |                100 | Using index                        |
| 4            | SUBQUERY              | s2              |                      | range          | idx_tss_classcode,idx_repeatcount_agentcom_CARDNUMBER_CLASSCODE,idx_repeatcount_agentcom_CARDNUMBER_agentcom,idx_agentcom_CARDNUMBER_CLASSCODE | idx_repeatcount_agentcom_CARDNUMBER_CLASSCODE | 60                |                       | 40148          |                 10 | Using index condition; Using where |
| 4            | SUBQUERY              | c               |                      | eq_ref         | PRIMARY                                                                                                                                        | PRIMARY                                       | 98                | pro_oedu.s2.CLASSCODE | 1              |                 10 | Using where                        |
| 3            | SUBQUERY              | s1              |                      | range          | idx_tss_classcode,idx_repeatcount_agentcom_CARDNUMBER_CLASSCODE,idx_repeatcount_agentcom_CARDNUMBER_agentcom,idx_agentcom_CARDNUMBER_CLASSCODE | idx_repeatcount_agentcom_CARDNUMBER_CLASSCODE | 60                |                       | 40148          |                100 | Using where; Using index           |
| 3            | SUBQUERY              | c               |                      | eq_ref         | PRIMARY                                                                                                                                        | PRIMARY                                       | 98                | pro_oedu.s1.CLASSCODE | 1              |                100 | Using index                        |
+--------------+-----------------------+-----------------+----------------------+----------------+------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------+-------------------+-----------------------+----------------+--------------------+------------------------------------+

总共执行了12个步骤 , 执行了1.2秒 ,其实相当快了,但是以后数据量上来后很难保证,这位同学要更快,要求真高阿

1. 这个语句的谓词有个特点:

第1相同部分: agentcom LIKE concat
第2相同部分: classstatus
第3相同部分: repeatcount
不相同部分: incompany

2. 每一次统计count 都要访问表一次 , 总共访问5次 , 要想办法去重 , 只访问一次表 , 使用case 语句做判断而做出统计数量出来 , 就是说case 一次匹配就加1 , 然后进行sum 代替count , 修改sql语句如下:

SELECT
    d.noRepeatStudentNumber,
    d.noRepeatTrainPassNumber,
    CONCAT ( round( 100 * ifnull( d.noRepeatTrainPassNumber / d.noRepeatStudentNumber, 0 ), 2 ), '%' ) AS noRepeatTrainPassRate,
    d.resultStudentNumber,
    d.trainPassNumber,
    CONCAT ( round( 100 * ifnull( d.trainPassNumber / d.resultStudentNumber, 0 ), 2 ), '%' ) AS trainPassRate,
    d.incompanyNumber,
    CONCAT ( round( 100 * ifnull( d.incompanyNumber / d.trainPassNumber, 0 ), 2 ), '%' ) AS incompanyRate 
FROM
    (
    SELECT
        sum( d1.noRepeatStudentNumber ) AS noRepeatStudentNumber,
        sum( d1.noRepeatTrainPassNumber ) AS noRepeatTrainPassNumber,
        sum( d1.resultStudentNumber ) AS resultStudentNumber,
        sum( d1.trainPassNumber ) AS trainPassNumber,
        sum( d1.incompanyNumber ) AS incompanyNumber 
    FROM
        (
        SELECT
            max( CASE WHEN s.repeatcount = '0' AND s.agentcom LIKE concat ( '8007', '%' ) THEN 1 ELSE 0 END ) AS noRepeatStudentNumber,
            max(
            CASE
                    WHEN s.repeatcount = '0' 
                    AND s.trainpass = '1' 
                    AND c.classstatus = '2' 
                    AND s.agentcom LIKE concat ( '8007', '%' ) THEN
                        1 ELSE 0 
                    END 
                    ) AS noRepeatTrainPassNumber,
                    max( CASE WHEN s.agentcom LIKE concat ( '8007', '%' ) THEN 1 ELSE 0 END ) AS resultStudentNumber,
                    max(
                    CASE
                            WHEN s.trainpass = '1' 
                            AND c.classstatus = '2' 
                            AND s.agentcom LIKE concat ( '8007', '%' ) THEN
                                1 ELSE 0 
                            END 
                            ) AS trainPassNumber,
                            max(
                            CASE
                                    WHEN s.trainpass = '1' 
                                    AND c.classstatus = '2' 
                                    AND s.incompany = '1' 
                                    AND s.agentcom LIKE concat ( '8007', '%' ) THEN
                                        1 ELSE 0 
                                    END 
                                    ) AS incompanyNumber 
                                FROM
                                    t_s_classes t
                                    INNER JOIN t_s_students s ON t.classcode = s.classcode 
                                GROUP BY
                                    s.cardnumber 
                                ORDER BY
                                NULL 
                                ) d1 
    ) d;

3. 性能瞬间0.5秒 

总结: 

        这是一个案例参考而已 , 优化sql  要观察其特点 . 

猜你喜欢

转载自www.cnblogs.com/kelvin19840813/p/10462302.html