我需要有关此SQL查询的帮助。我有一个包含以下模式的大表:
time_start
(时间戳) - 测量的开始时间,duration
(双倍) - 以秒为单位的测量持续时间,count_event1
(int) - 类型1的测量事件数,count_event2
(int) - 类型2的测量事件数我保证没有行重叠 - 在SQL中,没有两行time_start1 < time_start2 AND time_start1 + duration1 > time_start2
。
我想设计一个有效的SQL查询,它将测量按一些任意时间段(我称之为group_period
)进行分组,例如 3小时。我已经尝试过这样的事情:
SELECT
ROUND(time_start/group_period,0) AS time_period,
SUM(count_event1) AS sum_event1,
SUM(count_event2) AS sum_event2
FROM measurements
GROUP BY time_period;
然而,似乎存在问题。如果有duration
大于group_period
的衡量标准,我希望将此类测量值分组到它所属的所有时间段,但由于持续时间从未被考虑过,因此仅进行分组进入第一个。有办法解决这个问题吗?
性能是我关注的问题,因为随着时间的推移,我希望表的大小能够大幅增长,达到数百万,可能是数十或数亿行。您对索引或任何其他优化有任何建议,以提高此查询的速度吗?
答案 0 :(得分:0)
根据Timekiller的建议,我提出了以下问题:
ON DELETE CASCADE
它完全符合我的目标,所以任务完成了。但是,如果有人能够就以下条件对此查询的性能给出一些反馈,我仍然会感激:
-- Since there's a problem with declaring variables in PostgreSQL,
-- we will be using aliases for the arguments required by the script.
-- First some configuration:
-- group_period = 3600 -- group by 1 hour (= 3600 seconds)
-- min_time = 1440226301 -- Sat, 22 Aug 2015 06:51:41 GMT
-- max_time = 1450926301 -- Thu, 24 Dec 2015 03:05:01 GMT
-- Calculate the number of started periods in the given interval in advance.
-- period_count = CEIL((max_time - min_time) / group_period)
SET TIME ZONE UTC;
BEGIN TRANSACTION;
-- Create a temporary table and fill it with all time periods.
CREATE TEMP TABLE periods (period_start TIMESTAMP)
ON COMMIT DROP;
INSERT INTO periods (period_start)
SELECT to_timestamp(min_time + group_period * coefficient)
FROM generate_series(0, period_count) as coefficient;
-- Group data by the time periods.
-- Note that we don't require exact overlap of intervals:
-- A. [period_start, period_start + group_period]
-- B. [time_start, time_start + duration]
-- This would yield the best possible result but it would also slow
-- down the query significantly because of the part B.
-- We require only: period_start <= time_start <= period_start + group_period
SELECT
period_start,
COUNT(measurements.*) AS count_measurements,
SUM(count_event1) AS sum_event1,
SUM(count_event2) AS sum_event2
FROM periods
LEFT JOIN measurements
ON time_start BETWEEN period_start AND (period_start + group_period)
GROUP BY period_start;
COMMIT TRANSACTION;
表有大约5到8亿行。measurements
列是主键,并且具有唯一的btree索引。 time_start
和min_time
。我只知道将选择组时段以便max_time
。答案 1 :(得分:0)
(这对于评论来说太大了,所以我会将其作为答案发布。)
添加我对你的答案的评论,你可能应该首先获得最佳结果,如果结果变慢则优化。
至于性能,我在使用数据库时学到的一件事是你无法真正预测性能。高级DBMS中的查询优化器很复杂,并且在小型和大型数据集上的行为往往不同。你必须让你的表填满一些大的样本数据,试验索引并阅读EXPLAIN
的结果,没有别的办法。
有一些事情需要建议,但我知道Oracle优化器比Postgres好得多,所以其中一些可能不起作用。
如果您要检查的所有字段都包含在索引中,事情会更快。由于您正在执行左连接并且periods
是基础,因此可能没有理由对其进行索引,因为它将完全包含在内。 duration
应该包含在索引中,如果你要使用适当的间隔重叠 - 这样,Postgres就不必获取行来计算连接条件,索引就足够了。有可能它根本不会获取表行,因为它不需要除索引中存在的其他数据。我认为如果将它作为time_start
索引的第二个字段包含在内,它会表现得更好,至少在Oracle中它会如此,但IIRC Postgres能够将索引连接在一起,所以也许一秒钟index会表现得更好 - 您必须使用EXPLAIN
进行检查。
索引和数学混合不好。即使索引中包含duration
,也不能保证它会在(time_start + duration)
中使用 - 但是,请再次查看EXPLAIN
。如果没有使用它,尝试创建一个基于函数的索引(即,包括time_start + duration
作为字段),或者稍微改变表的结构,以便time_start + duration
是一个单独的列,而是索引该列。
如果你真的不需要左连接(也就是说你没有空缺时间),那么使用内连接 - 优化器可能会从一个更大的表开始(测量)和连接时间段它,可能使用散列连接而不是嵌套循环。如果你这样做,那么你也应该以相同的方式索引你的周期表,并且可能以相同的方式重构它,以便它显式包含开始和结束周期,因为优化器在不必执行时有更多选项列上的任何操作。
也许最重要的是,如果您有max_time
和min_time
,请在加入之前使用IT来限制measurements
的结果!您的设置越小,它的工作速度就越快。