2016-12-01 78 views
0

我在MySQL下表:MySQL的查找不同对每组值的时间间隔

CREATE TABLE `events` (
    `pv_name` varchar(60) COLLATE utf8mb4_bin NOT NULL, 
    `time_stamp` bigint(20) unsigned NOT NULL, 
    `event_type` varchar(40) COLLATE utf8mb4_bin NOT NULL, 
    `has_data` tinyint(1) NOT NULL, 
    `data` json DEFAULT NULL 
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=COMPRESSED; 

ALTER TABLE `events` 
ADD PRIMARY KEY (`pv_name`,`time_stamp`), ADD KEY `has_data` (`has_data`,`pv_name`,`time_stamp`); 

我们一直在努力以构建高效的查询,以找到具有在值的至少一个改变每个pv_name在给定的时间间隔内。

我相信,查询我公司目前已是低效的,因为它只要它发现找到所有在给定的时间间隔的不同值的每个pv_name,而不是停止不止一个:

SELECT events.pv_name 
FROM events 
WHERE events.time_stamp > 0 AND events.time_stamp < 9999999999999999999 
GROUP BY events.pv_name 
HAVING COUNT(DISTINCT JSON_EXTRACT(events.data, '$.value')) > 1; 

为了避免这种情况,我考虑拆分的数量和不同的部分为独立的步骤,因为该文件说:

当结合使用DISTINCT LIMIT ROW_COUNT,MySQL作为 找到独特的row_count行就停止。

是否有一个有效的查询找到在给定的时间间隔各pv_name一对不同的值,在没有找到所有为每个pv_name不同值在给定的时间间隔?

编辑@Rick詹姆斯

我基本上是试图找到这更快的非光标基础的解决方案:

SET @[email protected]@sql_mode, sql_mode='STRICT_ALL_TABLES'; 

DELIMITER // 

DROP PROCEDURE IF EXISTS check_for_change; 
CREATE PROCEDURE check_for_change(IN t0_in bigint(20) unsigned, IN t1_in bigint(20) unsigned) 
BEGIN 
    DECLARE done INT DEFAULT FALSE; 
    DECLARE current_pv_name VARCHAR(60); 
    DECLARE cur CURSOR FOR SELECT DISTINCT pv_name FROM events; 
    DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = TRUE; 

    SET @t0_in := t0_in; 
    SET @t1_in := t1_in; 


    IF @t0_in > @t1_in THEN 
     SET @temp := @t0_in; 
     SET @t0_in := @t1_in; 
     SET @t1_in := @temp; 
    END IF; 


    DROP TEMPORARY TABLE IF EXISTS has_change; 
    CREATE TEMPORARY TABLE has_change (
    pv_name varchar(60) NOT NULL, 
    PRIMARY KEY (pv_name) 
    ) ENGINE=Memory DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; 


    OPEN cur; 

    label1: LOOP 
     FETCH cur INTO current_pv_name; 

     IF done THEN 
      LEAVE label1; 
     END IF; 

     INSERT INTO has_change 
     SELECT current_pv_name 
     FROM (
     SELECT DISTINCT JSON_EXTRACT(events.data, '$.value') AS distinct_value 
     FROM events 
     WHERE events.pv_name = current_pv_name 
     AND events.has_data = 1 
     AND events.time_stamp > @t0_in AND events.time_stamp < @t1_in 
     LIMIT 2) AS t 
     HAVING COUNT(t.distinct_value) = 2; 
    END LOOP; 

    CLOSE cur; 
END // 

DELIMITER ; 

SET [email protected]_sql_mode; 

这里的优化是在限制对不同值的数量应用找到每个pv_name

+0

“不同的价值观”对我来说并不是说“找到每一个有变化的东西”。请提供一些样本数据和样本输出。 –

+0

如果它有一对值在一个区间中有所不同,那么这个值会在区间中改变。 – Patrick

+0

@RickJames我编辑了这个问题。我希望这有助于澄清它? – Patrick

回答

0

没有LIMIT,所以报价不适用。 (或者至少,我认为不是。)

COUNT(DISTINCT ...)将在某些情况下做一个“松散扫描”,这比读每一行更好。例如,

SELECT name 
    FROM tbl 
    GROUP BY name 
    HAVING COUNT(DISTINCT foo) > 3; 
INDEX(name, foo)

在一起可能会通过索引越级每个namefoosCOUNT DISTINCT。当然,这不是按照你的要求“停在3”。

您可以通过执行

FLUSH STATUS; 
SELECT ...; 
SHOW SESSIONS STATUS LIKE 'Handler%'; 

地看到,它没有(或没有)有一个Handler_read计数是表的大小证明上述。

由于多种原因,松散扫描不适用于您的特定查询。

底线:“不,你不能实现你的目标”。

此外,您写入的存储例程可能比简单地接受完整扫描的开销慢得多。

+0

我在说,如果我使用'COUNT(DISTINCT ...)',那么添加一个'LIMIT'并没有帮助,但是如果我创建一个单独的查询,只是使用'DISTINCT',那么我可以放一个'LIMIT'这将有助于(根据报价)。然后我可以对这个结果运行COUNT。这就是我在存储过程中所做的基本工作。 – Patrick

+0

这两个查询的相对速度似乎取决于要迭代的名称数量和时间间隔的大小。如果两者都很小,那么我发布的第一个查询更快,如果两者都很大,则光标方法更快,有时要快得多。 – Patrick

+0

作为一个问题,还有其他方法来改进基于游标的方法,即并行化吗? – Patrick