Php 如何使用Case语句优化具有多个sum的mysql查询,并按多个日期分组?
我正在尝试优化一个mysql查询,该查询具有多个和,每个和都有不同的case语句,可以按多个日期分组,也可以在每次运行查询的多个日期上循环。当前查询执行每次运行需要1.8-3.2秒 目前,我每次单独运行查询都会循环30个日期,即使是在快速端(每个查询1.8秒),也就是54秒运行30次查询 首先,我认为如果我可以按给定的日期范围对查询组进行分组,这将有助于优化开始的工作,但我不确定按给定的日期范围进行分组的最佳方式 其次,我确信我的表和/或查询本身可以优化 我提供了showcreatetabledetails、一个查询示例以及调用查询30次的php循环。如果还有什么可以帮忙的,请询问。我感谢您的帮助和反馈:) 表格详细信息: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> 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,我将对此进行测试。表索引如何?