MySQL检查时间范围内是否存在值变化

时间:2016-03-24 01:20:05

标签: mysql database

为简单起见,我在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;

我正在尝试找到最有效的查询来实现以下等效项:

给出一对时间戳t0t1

对于每个pv_name

  1. 从此行pv_name和最大time_stamp <= t0 (如果存在)的行中获取值。这是时间范围开始时的过程变量的值。如果此值无效,则将其丢弃。

  2. pv_nametime_stamp (t0, t1)中有效(如果存在)的行中获取值集。

    如果在1和2的组合值集合中有多个不同的值,则返回pv_name。

  3. 本质上,我试图找出哪个过程变量在给定时间范围内的值发生了变化,包括它在时间范围开始时的值的变化。

    表中有数十亿行的数量级,它将继续增长。表中有大约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或其他数据库,关系或其他方面使用相同或更高的效率吗?

1 个答案:

答案 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将其推迟到最后}}。您需要对这两种方法进行基准测试。