【发布时间】:2016-12-01 01:17:12
【问题描述】:
我在 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 查找的不同值数量的限制。
【问题讨论】:
-
“不同的值对”对我来说与“找到每个有变化的值”不同。请提供一些示例数据和示例输出。
-
如果有一对在一个区间内不同的值,比在区间内改变的值。
-
@RickJames 我编辑了这个问题。我希望这有助于澄清它?
标签: mysql performance group-by distinct