MySQL在时间间隔内为每个组查找不同的值对

时间:2016-12-01 01:17:13

标签: mysql performance group-by distinct

我在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;

为了避免这种情况,我正在考虑将计数和不同的部分分成不同的步骤,因为文档说:

  

当LIMIT row_count与DISTINCT组合时,MySQL会立即停止   它找到row_count唯一的行。

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

编辑@Rick James

我基本上是想为此找到一个更快的非基于游标的解决方案:

SET @old_sql_mode=@@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 sql_mode=@old_sql_mode;

此处的优化是对每个pv_name找到的不同值的数量限制的应用。

1 个答案:

答案 0 :(得分:0)

没有LIMIT,因此报价不适用。 (至少,我认为不是。)

在某些情况下,

COUNT(DISTINCT ...)会执行&#34;松散扫描&#34;,这比阅读每一行更好。例如,

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

INDEX(name, foo)一起可能会跳过索引,为COUNT DISTINCT执行foos name。当然,这不是&#34;停在3&#34;按照你的要求。

您可以通过

来演示上述内容
FLUSH STATUS;
SELECT ...;
SHOW SESSIONS STATUS LIKE 'Handler%';

要查看它没有(或没有)Handler_read计数是表的大小。

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

底线:&#34;不,你无法实现目标&#34;。

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