Mysql 是否可以提高此查询的性能

Mysql 是否可以提高此查询的性能,mysql,sql,performance,Mysql,Sql,Performance,我在MySql服务器中有一个非常慢的查询 我添加以下查询: SELECT CRR_DT, TOU, SRCE, SINK, NAME, SEASON, SRCESUMCONG, SINKSUMCONG, SRCEAVGCONG, SINKAVGCONG, SUMSINKMSRCE, AVGSINKMSRCE, HOURCOUNT, TERM, START_DT, END_DT, CTYPE, MW AS MW_AWARD, Mark, SCID

我在MySql服务器中有一个非常慢的查询

我添加以下查询:

SELECT  CRR_DT, TOU, SRCE, SINK, NAME, SEASON, SRCESUMCONG, SINKSUMCONG,
        SRCEAVGCONG, SINKAVGCONG, SUMSINKMSRCE, AVGSINKMSRCE,
        HOURCOUNT, TERM, START_DT, END_DT, CTYPE, MW AS MW_AWARD,
        Mark, SCID
    FROM  
      ( SELECT  a.CRR_DT, a.TOU, a.SRCE, a.SINK, a.NAME, a.SEASON, a.SRCESUMCONG,
                a.SINKSUMCONG, a.SRCEAVGCONG, a.SINKAVGCONG, a.SUMSINKMSRCE,
                a.AVGSINKMSRCE, a.HOURCOUNT, b.TERM, b.CTYPE, b.START_DT,
                b.END_DT, b.MW, b.SCID, b.Mark
            FROM  
              ( SELECT  CRR_DT, TOU, SRCE, SINK, NAME, SEASON, SRCESUMCONG, SINKSUMCONG,
                        SRCEAVGCONG, SINKAVGCONG, SUMSINKMSRCE, AVGSINKMSRCE,
                        HOURCOUNT
                    FROM  CRR_CONGCALC
                    WHERE  CRR_DT >= '2015-01'
              ) a
            INNER JOIN  
              ( SELECT  MARKET, TERM, TOU, SRCE, SINK, NAME, SCID, CTYPE, START_DT,
                        END_DT, SUM(MW) AS MW, SUBSTR(MARKET, 1, 3) AS MARK
                    FROM  CRR_INVENTORY
                    WHERE  COPTION = 'OBLIGATION'
                      AND  START_DT >= '2015-01-01'
                      AND  SCID IN ('EAGL' , 'LDES')
                    GROUP BY  MARKET , TOU , SRCE , SINK , NAME , SCID , CTYPE ,
                        START_DT , END_DT
              ) b ON a.NAME = b.NAME
              AND  a.TOU = b.TOU
      ) c
    WHERE  c.CRR_DT BETWEEN SUBSTR(c.START_DT, 1, 7) AND SUBSTR(c.END_DT, 1, 7 )
    ORDER BY  NAME , CRR_DT , TOU ASC
这里的结果是使用

我猜红色方块很危险。有人能帮我理解这个计划吗?一旦我有了这个执行计划,关于我应该检查什么的一些提示

编辑添加表布局
唤起回忆。对于数据库,“完整表扫描”意味着数据库不能使用任何东西来加速查询,它读取整个表这些行以非排序顺序存储,因此没有更好的方法“搜索”您要查找的员工id

这很糟糕。为什么?

如果您有一个包含一串列的表:
名字、姓氏、员工id,…,第50列
进行搜索,其中员工id=1234,如果员工id列上没有索引,则进行顺序扫描。如果您在表1.employee_id=table2.eid上执行
联接表2,则情况更糟,因为它必须将employee_id与联接表中的每个记录相匹配

如果创建索引,则可以大大减少查找匹配项(或丢弃非匹配项)的扫描时间,因为您可以搜索排序字段,而不是执行顺序扫描。快得多

当您在employee_id字段上创建索引时,您正在创建一种搜索员工编号的方法,该方法要快得多创建索引时,您会说“我将基于此字段加入,或者基于此字段使用where子句”。这会以占用少量磁盘空间为代价加快查询速度

索引有各种各样的技巧,你可以创建它们,使它们是唯一的,而不是唯一的,复合的(包含多个列)和各种各样的东西。发布您的查询,我们可以告诉您,为了加快速度,您可能会在索引方面做些什么


一个好的经验法则是,应该在where子句、联接条件或order by中使用的字段上的表上创建索引。选择字段取决于一些超出了本文讨论范围的事情,但这应该是一个开始。

让人回忆起来。对于数据库,“完整表扫描”意味着数据库不能使用任何东西来加速查询,它读取整个表这些行以非排序顺序存储,因此没有更好的方法“搜索”您要查找的员工id

这很糟糕。为什么?

如果您有一个包含一串列的表:
名字、姓氏、员工id,…,第50列
进行搜索,其中员工id=1234
,如果员工id列上没有索引,则进行顺序扫描。如果您在表1.employee_id=table2.eid上执行
联接表2,则情况更糟,因为它必须将employee_id与联接表中的每个记录相匹配

如果创建索引,则可以大大减少查找匹配项(或丢弃非匹配项)的扫描时间,因为您可以搜索排序字段,而不是执行顺序扫描。快得多

当您在employee_id字段上创建索引时,您正在创建一种搜索员工编号的方法,该方法要快得多创建索引时,您会说“我将基于此字段加入,或者基于此字段使用where子句”。这会以占用少量磁盘空间为代价加快查询速度

索引有各种各样的技巧,你可以创建它们,使它们是唯一的,而不是唯一的,复合的(包含多个列)和各种各样的东西。发布您的查询,我们可以告诉您,为了加快速度,您可能会在索引方面做些什么

一个好的经验法则是,应该在where子句、联接条件或order by中使用的字段上的表上创建索引。选择字段取决于一些超出本讨论范围的事情,但这应该是一个开始。

  • 从(选择…)连接(选择…)到…
的模式没有很好地优化。查看是否可以直接从其中一个表转到,而不是将其隐藏在子查询中

  • CRR\u CONGCALC
    需要
    索引(CRR\u DT)
    。(请提供
    显示创建表

  • CRR\u库存
    需要
    索引(COPION,START\u DT)

  • 如果需要,请进行更改,然后回来寻求更多建议。

    • 从(选择…)连接(选择…)到…的模式没有很好地优化。查看是否可以直接从其中一个表转到,而不是将其隐藏在子查询中

    • CRR\u CONGCALC
      需要
      索引(CRR\u DT)
      。(请提供
      显示创建表

    • CRR\u库存
      需要
      索引(COPION,START\u DT)


    如果需要,请进行这些更改,然后回来寻求更多建议。

    根据您的解释图,在
    CRR\u CONGCALC
    CRR\u INVENTORY
    上的每个子查询都会进行完整的表扫描。然后,当您将子查询连接在一起时,再进行一次完整表扫描,最后,当结果集排序时,再进行一次完整表扫描

    提高性能的几个技巧

  • 使用作为join语句、where子句、group by子句和order by子句一部分索引的字段。如果经常使用此查询,请考虑将索引添加到所有相关列。
  • 尽可能避免在联接中使用聚合操作的嵌套子查询。子查询返回的结果集没有索引,在其上连接将扫描整个表,而不仅仅是索引。此查询中的连接也可能导致奇怪且难以检测的扇出问题,但这不是您正在寻求解决方案的性能问题
    CREATE TABLE `CRR_CONGCALC` (
      `CRR_DT` varchar(7) NOT NULL,
      `TOU` varchar(50) NOT NULL,
      `SRCE` varchar(50) NOT NULL,
      `SINK` varchar(50) NOT NULL,
      `SRCESUMCONG` decimal(12,6) DEFAULT NULL,
      `SINKSUMCONG` decimal(12,6) DEFAULT NULL,
      `SRCEAVGCONG` decimal(12,6) DEFAULT NULL,
      `SINKAVGCONG` decimal(12,6) DEFAULT NULL,
      `SUMSINKMSRCE` decimal(12,6) DEFAULT NULL,
      `AVGSINKMSRCE` decimal(12,6) DEFAULT NULL,
      `HOURCOUNT` int(11) NOT NULL DEFAULT '0',
      `SEASON` char(1) NOT NULL DEFAULT '0',
      `NAME` varchar(110) NOT NULL,
      PRIMARY KEY (`CRR_DT`,`SRCE`,`SINK`,`TOU`,`HOURCOUNT`),
      KEY `srce_index` (`SRCE`),
      KEY `srcesink` (`SRCE`,`SINK`)
    ) 
    
    CREATE TABLE `CRR_INVENTORY` (
      `MARKET` varchar(50) NOT NULL,
      `TERM` varchar(50) NOT NULL,
      `TOU` varchar(50) NOT NULL,
      `INVENTORY_DT` date NOT NULL,
      `START_DT` datetime NOT NULL,
      `END_DT` datetime NOT NULL,
      `CRR_ID` varchar(50) NOT NULL,
      `NSR_INDEX` tinyint(1) NOT NULL,
      `SEGMENT` tinyint(1) NOT NULL,
      `CTYPE` varchar(50) NOT NULL,
      `CATEGORY` varchar(50) NOT NULL,
      `COPTION` varchar(50) NOT NULL,
      `SRCE` varchar(50) DEFAULT NULL,
      `SINK` varchar(50) DEFAULT NULL,
      `MW` decimal(8,4) NOT NULL,
      `SCID` varchar(50) NOT NULL,
      `SEASON` char(1) DEFAULT '0',
      `NAME` varchar(110) NOT NULL,
      PRIMARY KEY (`MARKET`,`INVENTORY_DT`,`CRR_ID`),
      KEY `srcesink` (`SRCE`,`SINK`)
    ) 
    
    c.CRR_DT BETWEEN SUBSTR(c.START_DT, 1, 7) AND SUBSTR(c.END_DT, 1, 7)
    
    DATE_FORMAT(<FIELD>, '%Y-%m-01') 
    
    SELECT CAST('2015-07-05' AS DATETIME) between '2015-07' and '2015-07';
    -- This query returns 0 == False.
    
    CREATE TABLE sub_a (KEY(CRR_DT), KEY(NAME), KEY(TOU), KEY(NAME, TOU)) AS
    SELECT CRR_DT,
           TOU,
           SRCE,
           SINK,
           NAME,
           SEASON,
           SRCESUMCONG,
           SINKSUMCONG,
           SRCEAVGCONG,
           SINKAVGCONG,
           SUMSINKMSRCE,
           AVGSINKMSRCE,
           HOURCOUNT
    FROM CRR_CONGCALC
    WHERE CRR_DT >= '2015-01-01';
    
    CREATE TABLE sub_b (KEY(NAME), KEY(TOU), KEY(NAME, TOU)) AS
    SELECT MARKET,
           TERM,
           TOU,
           SRCE,
           SINK,
           NAME,
           SCID,
           CTYPE,
           START_DT,
           END_DT,
           SUM(MW) AS MW_AWARD,
           SUBSTR(MARKET,1,3) AS MARK
     FROM CRR_INVENTORY
     WHERE COPTION = 'OBLIGATION'
       AND START_DT >= '2015-01-01'
       AND SCID IN ('EAGL','LDES')
     GROUP BY MARKET, TERM, TOU, 
              SRCE, SINK, NAME, SCID, 
              CTYPE, START_DT, END_DT, MARK
     -- note the two added columns in the groupby clause.
    
    SELECT a.CRR_DT, 
           a.TOU, 
           a.SRCE,
           a.SINK, 
           a.NAME, 
           a.SEASON, 
           a.SRCESUMCONG,
           a.SINKSUMCONG, 
           a.SRCEAVGCONG, 
           a.SINKAVGCONG, 
           a.SUMSINKMSRCE,
           a.AVGSINKMSRCE, 
           a.HOURCOUNT, 
           b.TERM, 
           b.CTYPE, 
           b.START_DT,
           b.END_DT,
           b.MW_AWARD,
           b.SCID, 
           b.Mark
    FROM sub_a a 
    JOIN sub_b b ON a.NAME = b.NAME AND a.TOU = b.TOU
    WHERE a.CRR_DT BETWEEN DATE_FORMAT(b.START_DT,'%Y-%m-01') 
      AND DATE_FORMAT(b.END_DT,'%Y-%m-01')
    ORDER BY NAME,
             CRR_DT,
             TOU;
    
    WHERE sub_a.CRR_DT BETWEEN DATE_FORMAT(sub_b.START_DT,'%Y-%m-01') 
          AND DATE_FORMAT(DATE_ADD(sub_b.END_DT, INTERVAL 1 MONTH),'%Y-%m-01')
    
      SELECT t.foo 
        FROM mytable t
       WHERE t.foo = 'bar'
    
      SELECT t.foo
        FROM (SELECT * FROM mytable) t
       WHERE t.foo = 'bar'
    
      SELECT t.foo
        FROM (SELECT * FROM mytable WHERE foo = 'bar') t
    
            SELECT c.crr_dt
                 , c.tou
                 , c.srce
                 , c.sink
                 , c.name
                 , c.season
                 , c.srcesumcong
                 , c.sinksumcong
                 , c.srceavgcong
                 , c.sinkavgcong
                 , c.sumsinkmsrce
                 , c.avgsinkmsrce
                 , c.hourcount
                 , b.term
                 , b.start_dt
                 , b.end_dt
                 , b.ctype
                 , b.mw AS mw_award
                 , b.scid
                 , b.mark
              FROM CRR_CONGCALC c
              JOIN ( SELECT i.market
                          , i.term
                          , i.tou
                          , i.srce
                          , i.sink
                          , i.name
                          , i.scid
                          , i.ctype
                          , i.start_dt
                          , i.end_dt
                          , SUM(i.mw) AS mw
                          , SUBSTR(i.market, 1, 3) AS mark
                       FROM CRR_INVENTORY i
                      WHERE i.coption = 'OBLIGATION'
                        AND i.start_dt >= '2015-01-01'
                        AND i.scid IN ('EAGL','LDES')
                      GROUP
                         BY i.market
                          , i.tou
                          , i.srce
                          , i.sink
                          , i.name
                          , i.scid
                          , i.ctype
                          , i.start_dt
                          , i.end_dt
                   ) b
                ON c.name = b.name
               AND c.tou  = b.tou
               AND c.crr_dt >= '2015-01'
               AND c.crr_dt BETWEEN SUBSTR(b.start_dt,1,7) 
                                AND SUBSTR(b.end_dt,1,7)
             ORDER
                BY c.name
                 , c.crr_dt
                 , c.tou
    
     AND c.crr_dt BETWEEN DATE_FORMAT(b.start_dt,'%Y-%m') AND DATE_FORMAT(b.end_dt,'%Y-%m')
    
                ON c.name = b.name
               AND c.tou  = b.tou
               AND c.crr_dt >= '2015-01'
               AND c.crr_dt BETWEEN SUBSTR(b.start_dt,1,7) 
                                AND SUBSTR(b.end_dt,1,7)