我正在尝试优化一个mysql查询,该查询具有多个总和,每个总和具有不同的case语句,并且可以按多个日期分组,或者每次循环运行查询的多个日期。当前查询执行每次运行需要1.8 - 3.2秒。
目前,我每次都会循环运行查询的30多个日期,即使是在快速一侧(每个查询1.8秒),也就是运行查询30次的54秒。
首先,我认为如果我可以在给定的日期范围内拥有查询组,这将有助于优化开始,但我不确定按给定日期范围进行分组的最佳方式。
其次,我确信我的表和/或查询本身可以进行优化。
我提供了SHOW CREATE TABLE详细信息,查询的一个示例,以及调用查询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)
EXPLAIN QUERY:
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 | |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------+
PHP代码在过去30天内循环:
$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>" ;
}
}
如果您对如何提高性能有任何见解,我将非常感激。感谢您花时间看看这个!
答案 0 :(得分:0)
计划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;
然后在稍微简化的查询中使用它来完成剩下的工作。我不知道这是否会有所帮助,但它可能会使查询更具可读性。