为简单起见,我在MySQL中有以下表格:
CREATE TABLE `events` (
`pv_name` varchar(60) NOT NULL,
`time_stamp` bigint(20) unsigned NOT NULL,
`value` text,
`value_valid` tinyint(1) NOT NULL,
PRIMARY KEY (`pv_name`,`time_stamp`),
) ENGINE=InnoDB;
我正在尝试找到最有效的查询来实现以下等效项:
给出一对时间戳t0
和t1
:
对于每个pv_name
:
从此行pv_name
和最大time_stamp <= t0
(如果存在)的行中获取值。这是时间范围开始时的过程变量的值。如果此值无效,则将其丢弃。
从pv_name
和time_stamp
(t0, t1)
中有效(如果存在)的行中获取值集。
如果在1和2的组合值集合中有多个不同的值,则返回pv_name。
本质上,我试图找出哪个过程变量在给定时间范围内的值发生了变化,包括它在时间范围开始时的值的变化。
表中有数十亿行的数量级,它将继续增长。表中有大约100,000个不同的pv_names,它们将保持相当静态。绝大多数相邻值(按每个pv_name的time_stamp排序)预计是不同的。
修改
如果我要从头开始实现这一点,我会执行以下操作:pv_names的集合将存储在trie中。 trie中每个pv_name的值将是指向二叉搜索树的链接。二叉搜索树将存储(time_stamp,value)的键值对。每个对中的值将是相应time_stamp处的pv_name的值。
要找出哪个pv_names的值在给定的time_range(t0,t1)中有变化,我将执行以下操作:遍历trie中的每个pv_name并按照其二叉搜索树的链接。在此树中找到小于或等于t0的最大time_stamp。如果不存在,则在此树中找到小于t1的最小time_stamp。如果这些都不存在则转到trie中的下一个pv_name。否则,以递增顺序迭代time_stamps,将与当前time_stamp相关联的值与与之前的time_stamp相关联的值进行比较。如果它们不同,请打印出pv_name。停止迭代time_stamps。转到trie中的下一个pv_name并重复。如果到达大于或等于t1的time_stamp,并且没有找到差异,则转到trie中的下一个pv_name并重复。不要在比较中使用time_stamp t1的值。
Simplified example:
pv_name | time_stamp | value
A | 1.0 | 1.15
B | 2.0 | 1.00
A | 3.0 | 1.12
B | 4.0 | 1.00
A | 5.0 | 1.00
B | 6.0 | 1.00
A | 7.0 | 3.15
B | 8.0 | 9.13
A | 9.0 | 4.30
B | 10.0 | 1.00
A | 11.0 | 9.00
B | 12.0 | 1.00
time range | values of A | values of B | result
(0.0,0.5) | NULL | NULL | NULL
(1.5,2.0) | 1.15 | NULL | NULL
(1.5,5.0) | 1.15, 1.12 | NULL, 1.00, 1.00 | A
(4.0,9.0) | 1.12, 1.00, 3.15 | 1.00, 1.00, 9.13 | A, B
(13.0,14.0) | 9.00 | 1.00 | NULL
我可以在MySQL或其他数据库,关系或其他方面使用相同或更高的效率吗?
答案 0 :(得分:0)
SELECT pv_name
FROM (
-- Query for step 1
SELECT e1.pv_name, e3.value
FROM (SELECT pv_name, MAX(time_stamp) AS start_time
FROM events
WHERE time_stamp <= @t0
GROUP BY pv_name) AS e1
JOIN events AS e3 ON e3.pv_name = e1.pv_name AND e3.time_stamp = e1.start_time
WHERE e3.value_valid
UNION DISTINCT
-- Query for step 2
SELECT DISTINCT pv_name, value
FROM events
WHERE time_stamp BETWEEN @t0 AND t1
AND value_valid
) AS x
GROUP BY pv_name
HAVING COUNT(*) > 1
联合中的第一个子查询使用SQL Select only rows with Max Value on a Column中的一种技术。我认为它应该是非常有效的,因为你的主键,但你可以尝试其他技术之一。
第二个子查询获取t0 - t1
时间范围内的所有valild行,删除重复值。
我不知道在子查询和UNION DISTINCT
中执行重复值抑制是否更有效,或者使用UNION ALL
和{{1将其推迟到最后}}。您需要对这两种方法进行基准测试。