Php 如何使用Case语句优化具有多个sum的mysql查询,并按多个日期分组?

Php 如何使用Case语句优化具有多个sum的mysql查询,并按多个日期分组?,php,mysql,sum,case,query-optimization,Php,Mysql,Sum,Case,Query Optimization,我正在尝试优化一个mysql查询,该查询具有多个和,每个和都有不同的case语句,可以按多个日期分组,也可以在每次运行查询的多个日期上循环。当前查询执行每次运行需要1.8-3.2秒 目前,我每次单独运行查询都会循环30个日期,即使是在快速端(每个查询1.8秒),也就是54秒运行30次查询 首先,我认为如果我可以按给定的日期范围对查询组进行分组,这将有助于优化开始的工作,但我不确定按给定的日期范围进行分组的最佳方式 其次,我确信我的表和/或查询本身可以优化 我提供了showcreatetabled

我正在尝试优化一个mysql查询,该查询具有多个和,每个和都有不同的case语句,可以按多个日期分组,也可以在每次运行查询的多个日期上循环。当前查询执行每次运行需要1.8-3.2秒

目前,我每次单独运行查询都会循环30个日期,即使是在快速端(每个查询1.8秒),也就是54秒运行30次查询

首先,我认为如果我可以按给定的日期范围对查询组进行分组,这将有助于优化开始的工作,但我不确定按给定的日期范围进行分组的最佳方式

其次,我确信我的表和/或查询本身可以优化

我提供了showcreatetabledetails、一个查询示例以及调用查询30次的php循环。如果还有什么可以帮忙的,请询问。我感谢您的帮助和反馈:)

表格详细信息:

mysql> SHOW CREATE TABLE accounts ;

    accounts | CREATE TABLE `accounts` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `verified` tinyint(1) DEFAULT '0',
  `active` int(1) unsigned NOT NULL DEFAULT '1',
  `clear` tinyint(1) unsigned DEFAULT '0',
  `email` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `batch` tinyint(1) unsigned NOT NULL DEFAULT '0',
  `batch_start` datetime DEFAULT NULL,
  `batch_complete` datetime DEFAULT NULL,
  `auth_failed_updated` datetime DEFAULT NULL,
  `checking_start` datetime DEFAULT NULL,
  `checking_complete` datetime DEFAULT NULL,
  `last_used` datetime DEFAULT NULL,
  `last_tested` datetime DEFAULT NULL,
  `creation_date` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `email` (`email`),
  KEY `batch_start` (`batch_start`),
  KEY `batch_complete` (`batch_complete`),
  KEY `last_used` (`last_used`),
  KEY `last_tested` (`last_tested`),
  KEY `active` (`active`),
  KEY `auth_failed_updated` (`auth_failed_updated`)
) ENGINE=MyISAM AUTO_INCREMENT=1422229 DEFAULT CHARSET=latin1
mysql>     SELECT  date(now()) as date,
        SUM(CASE WHEN date(acct.batch_start) = date(now()) THEN 1 ELSE 0 END) AS batch_start_count,
        SUM(CASE WHEN date(acct.batch_complete) = date(now()) THEN 1 ELSE 0 END) AS batch_complete_count,
        SUM(CASE WHEN (acct.batch_start IS NOT NULL
                      AND  acct.batch_complete IS NULL
                      AND  acct.active = 0
                      AND  date(acct.auth_failed_updated) = date(now()) ) THEN 1 ELSE 0 END
           ) AS batch_died_count,
        SUM(CASE WHEN (acct.batch = 3
                      AND  acct.last_used IS NULL
                      AND  date(acct.last_tested) = date(now())
                      AND  acct.last_used IS NULL) THEN 1 ELSE 0 END
           ) AS batch_died_unused_count,
        SUM(CASE WHEN (acct.batch = 3
                      AND  date(acct.last_used) = date(now())
                      AND  acct.active = 0) THEN 1 ELSE 0 END
           ) AS batch_died_used_count
    FROM  accounts acct
    GROUP BY  date
    ORDER BY  date ASC;

+------------+--------------------+-----------------------+-------------------+-------------------------+----------------------+
| date       | batch_start_count  | batch_complete_count  | batch_died_count  | batch_died_unused_count | batch_died_used_count |
+------------+--------------------+-----------------------+-------------------+-------------------------+----------------------+
| 2017-04-11 |               4040 |                   847 |              1856 |                      0  |                 1327 |
+------------+--------------------+-----------------------+-------------------+-------------------------+----------------------+
1 row in set (2.44 sec)
mysql> EXPLAIN (of that query)

+----+-------------+-------+------+---------------+------+---------+------+---------+-------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows    | Extra |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------+
|  1 | SIMPLE      | acct  | ALL  | NULL          | NULL | NULL    | NULL | 1421996 |       |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------+
$dates = array();
for($i = -30; $i < 1; $i++) {
    $the_date = date("Y-m-d", strtotime('-'. $i .' days ago'));
    $dates[] = $the_date ;
    $sql = "SELECT date('{$the_date}') as date, SUM(CASE WHEN date(acct.batch_start) = date('{$the_date}') THEN 1 ELSE 0 END) AS batch_start_count, SUM(CASE WHEN date(acct.batch_complete) = date('{$the_date}') THEN 1 ELSE 0 END) AS batch_complete_count, SUM(CASE WHEN (acct.batch_start IS NOT NULL AND acct.batch_complete IS NULL AND acct.active = 0 AND date(acct.auth_failed_updated) = date('{$the_date}') ) THEN 1 ELSE 0 END) AS batch_died_count, SUM(CASE WHEN (acct.batch = 3 AND acct.last_used IS NULL AND date(acct.last_tested) = date('{$the_date}') AND acct.last_used IS NULL) THEN 1 ELSE 0 END) AS batch_died_unused_count, SUM(CASE WHEN (acct.batch = 3 AND date(acct.last_used) = date('{$the_date}') AND acct.active = 0) THEN 1 ELSE 0 END) AS batch_died_used_count FROM accounts acct LEFT JOIN networks net ON net.id = acct.networks_id LEFT JOIN servers srv ON srv.id = net.servers_id GROUP BY date ORDER BY date ASC;" ; 
    if ($result = $mysqli->query($sql)) {
        $row = $result->fetch_assoc() ; 
        print_r($row) ;
        print "<br>" ; 
    }
}
单个查询示例:

mysql> SHOW CREATE TABLE accounts ;

    accounts | CREATE TABLE `accounts` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `verified` tinyint(1) DEFAULT '0',
  `active` int(1) unsigned NOT NULL DEFAULT '1',
  `clear` tinyint(1) unsigned DEFAULT '0',
  `email` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `batch` tinyint(1) unsigned NOT NULL DEFAULT '0',
  `batch_start` datetime DEFAULT NULL,
  `batch_complete` datetime DEFAULT NULL,
  `auth_failed_updated` datetime DEFAULT NULL,
  `checking_start` datetime DEFAULT NULL,
  `checking_complete` datetime DEFAULT NULL,
  `last_used` datetime DEFAULT NULL,
  `last_tested` datetime DEFAULT NULL,
  `creation_date` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `email` (`email`),
  KEY `batch_start` (`batch_start`),
  KEY `batch_complete` (`batch_complete`),
  KEY `last_used` (`last_used`),
  KEY `last_tested` (`last_tested`),
  KEY `active` (`active`),
  KEY `auth_failed_updated` (`auth_failed_updated`)
) ENGINE=MyISAM AUTO_INCREMENT=1422229 DEFAULT CHARSET=latin1
mysql>     SELECT  date(now()) as date,
        SUM(CASE WHEN date(acct.batch_start) = date(now()) THEN 1 ELSE 0 END) AS batch_start_count,
        SUM(CASE WHEN date(acct.batch_complete) = date(now()) THEN 1 ELSE 0 END) AS batch_complete_count,
        SUM(CASE WHEN (acct.batch_start IS NOT NULL
                      AND  acct.batch_complete IS NULL
                      AND  acct.active = 0
                      AND  date(acct.auth_failed_updated) = date(now()) ) THEN 1 ELSE 0 END
           ) AS batch_died_count,
        SUM(CASE WHEN (acct.batch = 3
                      AND  acct.last_used IS NULL
                      AND  date(acct.last_tested) = date(now())
                      AND  acct.last_used IS NULL) THEN 1 ELSE 0 END
           ) AS batch_died_unused_count,
        SUM(CASE WHEN (acct.batch = 3
                      AND  date(acct.last_used) = date(now())
                      AND  acct.active = 0) THEN 1 ELSE 0 END
           ) AS batch_died_used_count
    FROM  accounts acct
    GROUP BY  date
    ORDER BY  date ASC;

+------------+--------------------+-----------------------+-------------------+-------------------------+----------------------+
| date       | batch_start_count  | batch_complete_count  | batch_died_count  | batch_died_unused_count | batch_died_used_count |
+------------+--------------------+-----------------------+-------------------+-------------------------+----------------------+
| 2017-04-11 |               4040 |                   847 |              1856 |                      0  |                 1327 |
+------------+--------------------+-----------------------+-------------------+-------------------------+----------------------+
1 row in set (2.44 sec)
mysql> EXPLAIN (of that query)

+----+-------------+-------+------+---------------+------+---------+------+---------+-------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows    | Extra |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------+
|  1 | SIMPLE      | acct  | ALL  | NULL          | NULL | NULL    | NULL | 1421996 |       |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------+
$dates = array();
for($i = -30; $i < 1; $i++) {
    $the_date = date("Y-m-d", strtotime('-'. $i .' days ago'));
    $dates[] = $the_date ;
    $sql = "SELECT date('{$the_date}') as date, SUM(CASE WHEN date(acct.batch_start) = date('{$the_date}') THEN 1 ELSE 0 END) AS batch_start_count, SUM(CASE WHEN date(acct.batch_complete) = date('{$the_date}') THEN 1 ELSE 0 END) AS batch_complete_count, SUM(CASE WHEN (acct.batch_start IS NOT NULL AND acct.batch_complete IS NULL AND acct.active = 0 AND date(acct.auth_failed_updated) = date('{$the_date}') ) THEN 1 ELSE 0 END) AS batch_died_count, SUM(CASE WHEN (acct.batch = 3 AND acct.last_used IS NULL AND date(acct.last_tested) = date('{$the_date}') AND acct.last_used IS NULL) THEN 1 ELSE 0 END) AS batch_died_unused_count, SUM(CASE WHEN (acct.batch = 3 AND date(acct.last_used) = date('{$the_date}') AND acct.active = 0) THEN 1 ELSE 0 END) AS batch_died_used_count FROM accounts acct LEFT JOIN networks net ON net.id = acct.networks_id LEFT JOIN servers srv ON srv.id = net.servers_id GROUP BY date ORDER BY date ASC;" ; 
    if ($result = $mysqli->query($sql)) {
        $row = $result->fetch_assoc() ; 
        print_r($row) ;
        print "<br>" ; 
    }
}
解释查询:

mysql> SHOW CREATE TABLE accounts ;

    accounts | CREATE TABLE `accounts` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `verified` tinyint(1) DEFAULT '0',
  `active` int(1) unsigned NOT NULL DEFAULT '1',
  `clear` tinyint(1) unsigned DEFAULT '0',
  `email` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `batch` tinyint(1) unsigned NOT NULL DEFAULT '0',
  `batch_start` datetime DEFAULT NULL,
  `batch_complete` datetime DEFAULT NULL,
  `auth_failed_updated` datetime DEFAULT NULL,
  `checking_start` datetime DEFAULT NULL,
  `checking_complete` datetime DEFAULT NULL,
  `last_used` datetime DEFAULT NULL,
  `last_tested` datetime DEFAULT NULL,
  `creation_date` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `email` (`email`),
  KEY `batch_start` (`batch_start`),
  KEY `batch_complete` (`batch_complete`),
  KEY `last_used` (`last_used`),
  KEY `last_tested` (`last_tested`),
  KEY `active` (`active`),
  KEY `auth_failed_updated` (`auth_failed_updated`)
) ENGINE=MyISAM AUTO_INCREMENT=1422229 DEFAULT CHARSET=latin1
mysql>     SELECT  date(now()) as date,
        SUM(CASE WHEN date(acct.batch_start) = date(now()) THEN 1 ELSE 0 END) AS batch_start_count,
        SUM(CASE WHEN date(acct.batch_complete) = date(now()) THEN 1 ELSE 0 END) AS batch_complete_count,
        SUM(CASE WHEN (acct.batch_start IS NOT NULL
                      AND  acct.batch_complete IS NULL
                      AND  acct.active = 0
                      AND  date(acct.auth_failed_updated) = date(now()) ) THEN 1 ELSE 0 END
           ) AS batch_died_count,
        SUM(CASE WHEN (acct.batch = 3
                      AND  acct.last_used IS NULL
                      AND  date(acct.last_tested) = date(now())
                      AND  acct.last_used IS NULL) THEN 1 ELSE 0 END
           ) AS batch_died_unused_count,
        SUM(CASE WHEN (acct.batch = 3
                      AND  date(acct.last_used) = date(now())
                      AND  acct.active = 0) THEN 1 ELSE 0 END
           ) AS batch_died_used_count
    FROM  accounts acct
    GROUP BY  date
    ORDER BY  date ASC;

+------------+--------------------+-----------------------+-------------------+-------------------------+----------------------+
| date       | batch_start_count  | batch_complete_count  | batch_died_count  | batch_died_unused_count | batch_died_used_count |
+------------+--------------------+-----------------------+-------------------+-------------------------+----------------------+
| 2017-04-11 |               4040 |                   847 |              1856 |                      0  |                 1327 |
+------------+--------------------+-----------------------+-------------------+-------------------------+----------------------+
1 row in set (2.44 sec)
mysql> EXPLAIN (of that query)

+----+-------------+-------+------+---------------+------+---------+------+---------+-------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows    | Extra |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------+
|  1 | SIMPLE      | acct  | ALL  | NULL          | NULL | NULL    | NULL | 1421996 |       |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------+
$dates = array();
for($i = -30; $i < 1; $i++) {
    $the_date = date("Y-m-d", strtotime('-'. $i .' days ago'));
    $dates[] = $the_date ;
    $sql = "SELECT date('{$the_date}') as date, SUM(CASE WHEN date(acct.batch_start) = date('{$the_date}') THEN 1 ELSE 0 END) AS batch_start_count, SUM(CASE WHEN date(acct.batch_complete) = date('{$the_date}') THEN 1 ELSE 0 END) AS batch_complete_count, SUM(CASE WHEN (acct.batch_start IS NOT NULL AND acct.batch_complete IS NULL AND acct.active = 0 AND date(acct.auth_failed_updated) = date('{$the_date}') ) THEN 1 ELSE 0 END) AS batch_died_count, SUM(CASE WHEN (acct.batch = 3 AND acct.last_used IS NULL AND date(acct.last_tested) = date('{$the_date}') AND acct.last_used IS NULL) THEN 1 ELSE 0 END) AS batch_died_unused_count, SUM(CASE WHEN (acct.batch = 3 AND date(acct.last_used) = date('{$the_date}') AND acct.active = 0) THEN 1 ELSE 0 END) AS batch_died_used_count FROM accounts acct LEFT JOIN networks net ON net.id = acct.networks_id LEFT JOIN servers srv ON srv.id = net.servers_id GROUP BY date ORDER BY date ASC;" ; 
    if ($result = $mysqli->query($sql)) {
        $row = $result->fetch_assoc() ; 
        print_r($row) ;
        print "<br>" ; 
    }
}
PHP代码在过去30天内循环:

mysql> SHOW CREATE TABLE accounts ;

    accounts | CREATE TABLE `accounts` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `verified` tinyint(1) DEFAULT '0',
  `active` int(1) unsigned NOT NULL DEFAULT '1',
  `clear` tinyint(1) unsigned DEFAULT '0',
  `email` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `batch` tinyint(1) unsigned NOT NULL DEFAULT '0',
  `batch_start` datetime DEFAULT NULL,
  `batch_complete` datetime DEFAULT NULL,
  `auth_failed_updated` datetime DEFAULT NULL,
  `checking_start` datetime DEFAULT NULL,
  `checking_complete` datetime DEFAULT NULL,
  `last_used` datetime DEFAULT NULL,
  `last_tested` datetime DEFAULT NULL,
  `creation_date` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `email` (`email`),
  KEY `batch_start` (`batch_start`),
  KEY `batch_complete` (`batch_complete`),
  KEY `last_used` (`last_used`),
  KEY `last_tested` (`last_tested`),
  KEY `active` (`active`),
  KEY `auth_failed_updated` (`auth_failed_updated`)
) ENGINE=MyISAM AUTO_INCREMENT=1422229 DEFAULT CHARSET=latin1
mysql>     SELECT  date(now()) as date,
        SUM(CASE WHEN date(acct.batch_start) = date(now()) THEN 1 ELSE 0 END) AS batch_start_count,
        SUM(CASE WHEN date(acct.batch_complete) = date(now()) THEN 1 ELSE 0 END) AS batch_complete_count,
        SUM(CASE WHEN (acct.batch_start IS NOT NULL
                      AND  acct.batch_complete IS NULL
                      AND  acct.active = 0
                      AND  date(acct.auth_failed_updated) = date(now()) ) THEN 1 ELSE 0 END
           ) AS batch_died_count,
        SUM(CASE WHEN (acct.batch = 3
                      AND  acct.last_used IS NULL
                      AND  date(acct.last_tested) = date(now())
                      AND  acct.last_used IS NULL) THEN 1 ELSE 0 END
           ) AS batch_died_unused_count,
        SUM(CASE WHEN (acct.batch = 3
                      AND  date(acct.last_used) = date(now())
                      AND  acct.active = 0) THEN 1 ELSE 0 END
           ) AS batch_died_used_count
    FROM  accounts acct
    GROUP BY  date
    ORDER BY  date ASC;

+------------+--------------------+-----------------------+-------------------+-------------------------+----------------------+
| date       | batch_start_count  | batch_complete_count  | batch_died_count  | batch_died_unused_count | batch_died_used_count |
+------------+--------------------+-----------------------+-------------------+-------------------------+----------------------+
| 2017-04-11 |               4040 |                   847 |              1856 |                      0  |                 1327 |
+------------+--------------------+-----------------------+-------------------+-------------------------+----------------------+
1 row in set (2.44 sec)
mysql> EXPLAIN (of that query)

+----+-------------+-------+------+---------------+------+---------+------+---------+-------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows    | Extra |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------+
|  1 | SIMPLE      | acct  | ALL  | NULL          | NULL | NULL    | NULL | 1421996 |       |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------+
$dates = array();
for($i = -30; $i < 1; $i++) {
    $the_date = date("Y-m-d", strtotime('-'. $i .' days ago'));
    $dates[] = $the_date ;
    $sql = "SELECT date('{$the_date}') as date, SUM(CASE WHEN date(acct.batch_start) = date('{$the_date}') THEN 1 ELSE 0 END) AS batch_start_count, SUM(CASE WHEN date(acct.batch_complete) = date('{$the_date}') THEN 1 ELSE 0 END) AS batch_complete_count, SUM(CASE WHEN (acct.batch_start IS NOT NULL AND acct.batch_complete IS NULL AND acct.active = 0 AND date(acct.auth_failed_updated) = date('{$the_date}') ) THEN 1 ELSE 0 END) AS batch_died_count, SUM(CASE WHEN (acct.batch = 3 AND acct.last_used IS NULL AND date(acct.last_tested) = date('{$the_date}') AND acct.last_used IS NULL) THEN 1 ELSE 0 END) AS batch_died_unused_count, SUM(CASE WHEN (acct.batch = 3 AND date(acct.last_used) = date('{$the_date}') AND acct.active = 0) THEN 1 ELSE 0 END) AS batch_died_used_count FROM accounts acct LEFT JOIN networks net ON net.id = acct.networks_id LEFT JOIN servers srv ON srv.id = net.servers_id GROUP BY date ORDER BY date ASC;" ; 
    if ($result = $mysqli->query($sql)) {
        $row = $result->fetch_assoc() ; 
        print_r($row) ;
        print "<br>" ; 
    }
}
$dates=array();
对于($i=-30;$i<1;$i++){
$the_date=date(“Y-m-d”,strotime('-'.$i.'days ago');
$dates[]=$the_日期;
$sql=“选择日期(“{$theu date}”)作为日期,求和(CASE WHEN date(acct.batch_start)=date(“{$theu date}”)然后1 ELSE 0 END)作为批次开始计数,求和(CASE WHEN date(acct.batch_complete)=date date(“{$theu date}”)然后1 ELSE 0 END)作为批次完成计数,求和(CASE WHEN date acct.batch_complete)=日期(“{$theu date END”)(acct.batch_start不为NULL,acct.batch_complete为NULL,acct.active=0,date(acct.auth_failed_updated)=date('{$the_date}')),然后1 ELSE 0 END)作为批次_dead_count,SUM(acct.batch=3,acct.last_使用为NULL,date(acct.last_tested)=date('{$the_date}'),acct.last_使用为NULL),然后1 ELSE 0 END)作为批次死亡未使用计数,求和(当(acct.batch=3和date(acct.last_used)=date(“{$the_date}”)和acct.active=0时的情况),然后1或0结束)作为批次死亡未使用计数,从帐户帐户开始计算网络上的左连接网络。id=acct.networks\u id左连接服务器srv ON srv.id=net.servers\u id按日期排序按日期分组ASC;“;
如果($result=$mysqli->query($sql)){
$row=$result->fetch_assoc();
打印(行);
打印“
”; } }
如果您对我如何提高绩效有任何见解,我将不胜感激。感谢您抽出时间研究此问题!

计划A:

  • 用你需要的日期建立一个小表格
  • JOIN
    到该表,用该表中的日期替换
    date(now())
    的所有实例
  • 代码的其余部分可能还可以

    另一个较小的优化是要注意,它们给出了相同的值:

     SUM(CASE WHEN boolean_expr) THEN 1 ELSE 0 END
     SUM(          boolean_expr)
    
    这是因为
    TRUE
    被视为
    1

    方案B:

    CREATE TEMPORARY TABLE t
        SELECT  DATE(batch_start) AS batch_start,  -- Note this may stay NULL
                DATE( ... etc  ...
                active,
                batch_complete IS NULL AS incomplete,
                batch
            FROM accounts;
    

    然后在一个稍微简化的查询中使用它来完成剩下的部分。我不知道这是否会有帮助,但它可能会使查询更具可读性。

    FYI:date(now())=CURDATE(),将节省几毫秒的时间hanks@nogad,我将对此进行测试。表索引如何?