我正在尝试为每个ID显示一个表,以查看是否有可用的数据。在这种情况下,它是测量。如果没有数据,则表示记录器未正常工作。
我目前有两个表:data
和times
。 data
包含id
,datetime
,sensor_id
和value
。 times
包含id
和value
。
时间充满00:00
,00:01
,00:02
等,一直到23:59
。
我有这个问题:
SELECT t.`value`, d.`sensor_id`, COUNT(t.`value`) as `numrows`
FROM `data` d
RIGHT JOIN `times` t
ON d.`datetime` LIKE CONCAT('% ', t.`value`, '%')
WHERE d.`datetime` LIKE '%$2014-11-05%'
AND d.`sensor_id` IN(1,2,3,4,5,999)
GROUP BY d.`sensor_id`, t.`value`
ORDER BY d.`sensor_id` ASC, t.`id` ASC
while($s = $select->fetch_assoc()) {
$checkArray[$s['sensor_id']][$s['value']] = $s['numrows'];
}
foreach($checkArray as $key => $arr) {
echo 'Sensor: ' . $key;
for($i = 0; $i <= 23; $i++) {
for($j = 0; $j <= 59; $j++) {
$time = strlen($i) == 1 ? '0' . $i : '' . $i;
$time .= ':';
$time .= strlen($j) == 1 ? '0' .$j : '' . $j;
if(isset($arr[$time]) && $arr[$time] >= 1) { //See if has at least one row
echo 'YES DATA FOR ' . $time . '<br>';
}
}
}
}
当然,我在表格中对此进行排序,结果如下:
仅适用于传感器1,2,3,4和5,加载时间超过5.5秒。我不知道如何进一步优化这一点。我在查询的列上放了索引,但我想不出别的什么。
我的SHOW CREATE TABLE
data
:
CREATE TABLE `data` (
`id` int(13) NOT NULL AUTO_INCREMENT,
`sensor_id` int(13) NOT NULL,
`datetime` datetime NOT NULL,
`value` float NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `sensor_id_2` (`sensor_id`,`datetime`,`value`),
KEY `sensor_id` (`sensor_id`),
KEY `value` (`value`),
KEY `datetime` (`datetime`),
KEY `sensor_id_3` (`sensor_id`),
KEY `datetime_2` (`datetime`)
) ENGINE=InnoDB AUTO_INCREMENT=103921 DEFAULT CHARSET=utf8
答案 0 :(得分:2)
您的查询会遇到困难,因为它尝试使用前导通配符对LIKE进行连接。没有索引对此有用。
进一步检查WHERE子句中datetime的值,再次使用前导通配符,这将阻止在该检查上使用索引。此外,您正在检查RIGHT JOINed表上的值,有效地将其呈现为INNER JOIN。
我会尝试这样的事情: -
SELECT sub0.aDateTime, sub1.sensor_id, COUNT(d.datetime)
FROM
(
SELECT DATE_ADD('2014-11-05 00:00:00', INTERVAL a.Mnt + b.Mnt * 10 + c.Mnt * 100 + d.Mnt * 1000 MINUTE) AS aDateTime,
DATE_ADD('2014-11-05 00:00:59', INTERVAL a.Mnt + b.Mnt * 10 + c.Mnt * 100 + d.Mnt * 1000 MINUTE) AS aDateTimeEnd
FROM (SELECT 0 AS Mnt UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) a
CROSS JOIN (SELECT 0 AS Mnt UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) b
CROSS JOIN (SELECT 0 AS Mnt UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) c
CROSS JOIN (SELECT 0 AS Mnt UNION SELECT 1) d
WHERE (a.Mnt + b.Mnt * 10 + c.Mnt * 100 + d.Mnt * 1000) < 1440
) sub0
CROSS JOIN
(SELECT 1 AS sensor_id UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 999) sub1
LEFT OUTER JOIN data d
ON d.datetime BETWEEN sub0.aDateTime AND sub0.aDateTimeEnd
AND d.sensor_id = sub1.sensor_id
GROUP BY sub1.sensor_id, sub0.aDateTime
ORDER BY sub1.sensor_id ASC, sub0.aDateTime ASC
这生成一系列从0到1439的数字(即一天中的分钟数),并将其添加到您感兴趣的那一天的开始。这样就可以为当天的每一分钟生成一行(返回的2个日期/时间值是分钟的第一个和最后一个秒)。然后LEFT OUTER将数据连接到基于date_time的数据。
如果您只有一个数字1到1439的表并且将数据连接到数据以保存子查询(这仍然需要一个函数来计算日期,但仍然节省了一点时间),这可以很容易地改进)。
答案 1 :(得分:1)
您可以解决以下问题:
SELECT t.value, d.sensor_id, COUNT(t.value) as numrows
FROM times t LEFT JOIN
data d
ON date_format(d.datetime, '%H:%i') = t.value
WHERE d.datetime >= date('2014-11-05') and
d.datetime < date('2014-11-06') and
d.sensor_id IN (1, 2, 3, 4, 5, 999)
GROUP BY d.sensor_id, t.value
ORDER BY d.sensor_id ASC, t.value ASC;
请特别注意,此查询在日期/时间列上不使用like
。这迫使他们转换为字符串并排除使用索引。