所以我有一个相当大的表格,精确的价格滴答(使用MariaDB)。
CREATE TABLE `table` (
`num` int(11) NOT NULL AUTO_INCREMENT,
`datetime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`pairs` varchar(40) NOT NULL,
`price` decimal(16,10) NOT NULL,
`volume` decimal(22,10) NOT NULL,
PRIMARY KEY (`num`),
KEY `datetime_pairs` (`pairs`,`datetime`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1
当数据超过x天时,我希望按条目类型对这些价格进行每小时平均值。在这个例子中,我需要7天。我提出了这个问题。
SELECT `num`, `datetime`, `pairs`, `price`, `volume`,
AVG(`price`) AS `priceAVG`, AVG(`volume`) AS `volumeAVG`
FROM table
WHERE DATE_FORMAT(`datetime`, '%Y-%m-%d %H:00:00')
< DATE_FORMAT(DATE_SUB(NOW(),INTERVAL 7 DAY), '%Y-%m-%d %H:00:00')
GROUP BY DATE_FORMAT(`datetime`, '%Y-%m-%d %H:00:00'), `pairs`
查询需要大约25秒才能运行。我认为我不能优化它。 这可能打印出我正在寻找的结果......但是,一旦我有数据可以使用,我真的不确定回答我的问题的最佳做法是什么。
插入此结果并删除旧数据?它会弄乱主键号num
,使其与datetime
的排序方式不一致。
使用SELECT查询的结果更新旧数据,并删除旧数据减去那些更新的行?这就是我现在想要实现的目标......
我认为有重复的表可能不是一个选项,因为我有一百个这样的表来处理,而cpu ressources也是需要考虑的事情。我正在使用cron和php来转换这些查询。我可能每12或24小时进行一次这种操作。
在这种情况下,适合的方法是什么?
更新查询是否是处理此问题的现实方法?
答案 0 :(得分:0)
DATE_FORMAT()
的字符串效率不高。直接比较日期。datetime
字段中没有索引。不,datetime_pairs
不算数,因为:
pairs
没有参与。datetime
不是复合键中的第一项。所以:
CREATE TABLE `table` (
`num` int(11) NOT NULL AUTO_INCREMENT,
`datetime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`pairs` varchar(40) NOT NULL,
`price` decimal(16,10) NOT NULL,
`volume` decimal(22,10) NOT NULL,
PRIMARY KEY (`num`),
KEY `datetime` (`datetime`), -- change
KEY `pairs` (`pairs`) -- change
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1
和
SELECT `num`, `datetime`, `pairs`, `price`, `volume`,
AVG(`price`) AS `priceAVG`, AVG(`volume`) AS `volumeAVG`
FROM table
-- DATE(datetime) is the same as 00:00:00 on that day, but is
-- a DATE type that can be efficiently compared
WHERE `datetime` < DATE(DATE_SUB(NOW(),INTERVAL 7 DAY)
GROUP BY DATE(`datetime`), `pairs`
你仍然会被GROUP BY DATE(datetime)
约束,因为没有索引可供使用。您可能希望添加简单date DATE NOT NULL
的anoter列,以便您可以利用它上面的索引,尽管上面应该已经大大减少了所需的时间。
此外,查询中的列num, datetime, price, volume
几乎没有意义,因为它们未在GROUP BY
语句中引用。
此外,历史数据不会发生变化,因此您不应该多次聚合每日数据。存储平均值和计数等每日聚合量足以构建更大的聚合。例如:
SELECT AVG(daily_count * daily_average) AS 'weekly_average'
FROM daily_aggregates
WHERE datestamp > DATE(NOW() - INTERVAL 7 DAY)
利用缓存。我已经看到了很多的性能问题,因为开发人员在从未更改的巨型历史数据集上反复运行完全相同的计算。只需像缓存的每日聚合这样简单的事情就可以将数据集从每年数千或数百万项减少到365.
答案 1 :(得分:0)
你似乎也需要时间,而不仅仅是日期?因此,只是比较日期部分对我没有帮助。
添加persistent generated column,从datetime
为您提供包含小时但没有分钟或秒数(日期设置为0)的日期。
ALTER TABLE `table`
ADD (`date_to_the_hour` date AS (date(`datetime`) + INTERVAL hour(`datetime`) HOUR) PERSISTENT);
在该列上添加一个索引pairs
。
CREATE INDEX `date_to_the_hour_pairs`
ON `table`
(`date_to_the_hour`,
`pairs`);
更改您的选择以与新列进行比较。
SELECT `num`,
`datetime`,
`pairs`,
`price`,
`volume`,
AVG(`price`) `priceAVG`,
AVG(`volume`) `volumeAVG`
FROM `table`
WHERE `date_to_the_hour` < date(now() - INTERVAL 7 DAY) + INTERVAL hour(now()) HOUR
GROUP BY `date_to_the_hour`,
`pairs`;
这可能会加快速度。
答案 2 :(得分:0)
使用11字节decimal(22,10)
代替简单的4字节FLOAT
的任何理由?
在执行num
时选择datetime
,pairs
,price
,volume
,GROUP BY
没有意义
构建并维护一个按小时分解的摘要表。然后从该表构建报告。 http://mysql.rjweb.org/doc.php/summarytables
两个条目是否有相同(datetime, pairs)
组合的变化?如果没有删除id
并将其设为2列PRIMARY KEY
。
同时,您可以部分优化查询
过去一周的小时平均值,但不包括当前小时数:
SELECT LEFT(`datetime`, 13) AS the_hour,
pairs,
AVG(`price`) AS `priceAVG`,
AVG(`volume`) AS `volumeAVG`
FROM `table`
WHERE `datetime` >= DATE_FORMAT(NOW() - INTERVAL 7 DAY), '%Y-%m-%d %H:00:00')
AND `datetime` < DATE_FORMAT(NOW(), '%Y-%m-%d %H:00:00')
GROUP BY LEFT(`datetime`, 13), pairs
并且
INDEX(datetime)
汇总表方法是最复杂的,但是为您提供最大的帮助。