Mysql 更高效的分组方式,用于带有案例的查询

Mysql 更高效的分组方式,用于带有案例的查询,mysql,performance,group-by,Mysql,Performance,Group By,下面的查询构建了一个记录集,该记录集在饼图中用作报告 它并不经常运行,但当它运行时需要几秒钟,我想知道是否有任何方法可以提高它的效率 SELECT CASE WHEN (lastStatus IS NULL) THEN 'Unused' WHEN (attempts > 3 AND callbackAfter IS NULL) THEN 'Max Attempts Reached' WHEN (callbackAfter IS NOT NULL AN

下面的查询构建了一个记录集,该记录集在饼图中用作报告

它并不经常运行,但当它运行时需要几秒钟,我想知道是否有任何方法可以提高它的效率

SELECT
  CASE
    WHEN (lastStatus IS NULL)     THEN 'Unused'
    WHEN (attempts > 3 AND callbackAfter IS NULL)   THEN 'Max Attempts Reached'
    WHEN (callbackAfter IS NOT NULL AND callbackAfter >  DATE_ADD(NOW(), INTERVAL 7 DAY)) THEN 'Call Back After 7 Days'
    WHEN (callbackAfter IS NOT NULL AND callbackAfter <= DATE_ADD(NOW(), INTERVAL 7 DAY)) THEN 'Call Back Within 7 Days'
    WHEN (archived = 0)     THEN 'Call Back Within 7 Days'
    ELSE 'Spoke To'
  END AS statusSummary,
  COUNT(leadId) AS total
FROM
  CO_Lead
WHERE
  groupId = 123
  AND
  deleted = 0
GROUP BY
  statusSummary
ORDER BY
  total DESC;

尝试删除索引以查看这是否会提高性能

在某些数据库中,索引不一定能提高性能。如果您有索引,MySQL将始终使用它。在这种情况下,这意味着它将读取索引,然后必须从每个页面读取数据。页面读取是随机的,而不是顺序的。对于无论如何都必须读取所有页面的查询,这种随机读取会降低性能。

注意:

  • 如果
    leadId
    不能为
    NULL
    ,则将
    COUNT(leadId)
    更改为
    COUNT(*)
    。它们在逻辑上是等价的,但MySQL优化器的大多数版本并不能很好地识别这一点
  • 删除两个冗余的
    callbackAfter IS NOT NULL
    条件。如果
    callbackAfter
    满足第二部分,则无论如何不能为null
  • 您可以从将查询拆分为6个部分并为每个部分添加适当的索引中获益—但取决于
    案例
    中的条件是否重叠,您可能会得到错误或正确的结果
  • 可能的重写(注意不同的格式,检查是否返回相同的结果,可能不会!)


    leadID
    列中是否有空值?否,leadID是主键(自动递增)您可以添加表定义吗?谢谢@ypercube,我尝试了类似于您的查询的方法,但错过了添加额外索引的步骤。你的另外两点帮助我整理代码。不过,我可能会先尝试广域覆盖索引,看看这是否有区别。您还可以检查5个子查询中的每一个子查询,看看它们是否真正等同于
    CASE
    表达式。我会将它们写为等价的,但条件会更复杂,不可搜索。谢谢,不幸的是,使用此表时有几件事情需要索引,因此我无法将它们全部删除,删除一些可能会导致使用比当前效率更低的索引。但我认为这值得为其他人尝试。
    CREATE TABLE CO_Lead (
      objectId                             int UNSIGNED       NOT NULL AUTO_INCREMENT,
      groupId                              int UNSIGNED       NOT NULL,
      numberToCall                         varchar(20)        NOT NULL,
      firstName                            varchar(100)       NOT NULL,
      lastName                             varchar(100)       NOT NULL,
      attempts                             tinyint            NOT NULL default 0,
      callbackAfter                        datetime           NULL,
      lastStatus                           varchar(30)        NULL,
      createdDate                          datetime           NOT NULL,
      archived                             bool               NOT NULL default 0,
      deleted                              bool               NOT NULL default 0,
      PRIMARY KEY (
        objectId
      )
    ) ENGINE = InnoDB;
    ALTER TABLE CO_Lead ADD CONSTRAINT UQIX_CO_Lead UNIQUE INDEX (
      objectId
    );
    ALTER TABLE CO_Lead ADD INDEX (
      groupId,
      archived,
      deleted,
      callbackAfter,
      attempts
    );
    ALTER TABLE CO_Lead ADD INDEX (
      groupId,
      deleted,
      createdDate,
      lastStatus
    );
    ALTER TABLE CO_Lead ADD INDEX (
      firstName
    );
    ALTER TABLE CO_Lead ADD INDEX (
      lastName
    );
    ALTER TABLE CO_Lead ADD INDEX (
      lastStatus
    );
    ALTER TABLE CO_Lead ADD INDEX (
      createdDate
    );
    
    SELECT
        cnt1 AS "Unused"
      , cnt2 AS "Max Attempts Reached"
      , cnt3 AS "Call Back After 7 Days"
      , cnt4 AS "Call Back Within 7 Days"
      , cnt5 AS "Call Back Within 7 Days"
      , cnt6 - (cnt1+cnt2+cnt3+cnt4+cnt5) AS "Spoke To"
    FROM
      ( SELECT
          ( SELECT COUNT(*)  FROM CO_Lead
            WHERE groupId = 123 AND deleted = 0
              AND lastStatus IS NULL
          ) AS cnt1
        , ( SELECT COUNT(*)  FROM CO_Lead
            WHERE groupId = 123 AND deleted = 0
              AND attempts > 3 AND callbackAfter IS NULL
          ) AS cnt2
        , ( SELECT COUNT(*)  FROM CO_Lead
            WHERE groupId = 123 AND deleted = 0
              AND callbackAfter >  DATE_ADD(NOW(), INTERVAL 7 DAY)
          ) AS cnt3
        , ( SELECT COUNT(*)  FROM CO_Lead
            WHERE groupId = 123 AND deleted = 0
              AND callbackAfter <= DATE_ADD(NOW(), INTERVAL 7 DAY)
          ) AS cnt4
        , ( SELECT COUNT(*)  FROM CO_Lead
            WHERE groupId = 123 AND deleted = 0
              AND archived = 0
          ) AS cnt5
        , ( SELECT COUNT(*)  FROM CO_Lead
            WHERE groupId = 123 AND deleted = 0
          ) AS cnt6
      ) AS tmp ;
    
     (groupId, deleted, lastStatus, callbackAfter, attempts, archived)