Mysql 基于列计算非连续行集的优化方法

Mysql 基于列计算非连续行集的优化方法,mysql,sql,query-optimization,Mysql,Sql,Query Optimization,原件: 我会稍微改变一下结构,这样我想做的事情就更明显了 鉴于: +------+---------------+---------------+----+ | guid | current_level | current_value | pk | +------+---------------+---------------+----+ | a | 100 | 12 | 1 | | a | 200 |

原件:

我会稍微改变一下结构,这样我想做的事情就更明显了

鉴于:

+------+---------------+---------------+----+
| guid | current_level | current_value | pk |
+------+---------------+---------------+----+
| a    |           100 |            12 |  1 |
| a    |           200 |            12 |  2 |
| a    |           200 |            12 |  3 |
| a    |           200 |            12 |  4 |
| a    |           300 |            14 |  7 |
| a    |           300 |            12 |  9 |
| a    |           200 |            14 | 12 |
| b    |           100 |            10 |  5 |
| b    |           100 |            10 |  8 |
| b    |           200 |            12 | 11 |
| b    |           100 |            12 | 13 |
| b    |           200 |            12 | 14 |
| b    |           300 |            12 | 15 |
| b    |           200 |            12 | 16 |
+------+---------------+---------------+----+
我想计算每个guid进入级别200的总次数,忽略它停留在200的行。因此,级别200的连续行应视为1,而从200->100或300->200的转换应视为2

鉴于上述结构,我正在寻找的结果是:

+------+-------+-------+
| guid | level | times |
+------+-------+-------+
| a    |   200 |     2 |
| b    |   200 |     3 |
+------+-------+-------+
上面链接的原始问题在技术上是可行的,但是,当在一个有1.8M行的表上使用该解决方案时,大约需要30秒,这比最优值要少

注意:解决方案sq的内部子查询往往需要不到一秒钟的时间,但是整个查询的性能很差。如果有人能解释为什么会这样,我将不胜感激,可能是因为temp表太大了吧

问题是,考虑到表的大小,什么是实现我尝试的目标的有效方法

旧查询供参考:

SELECT guid, SUM(TIMES) FROM (
    SELECT  guid, current_level ,
            if(@id <> guid, @lev := 10, 0) AS useless,
            if(@id <> guid, @id := guid, 0) AS useless2,
            (case when (current_level = 200
                    AND current_level <> @lev) then 1 else 0 end) as TIMES,
            if(current_level <> @lev, @lev := current_level, 0) AS useless3

     FROM sensor_logs
     , (SELECT @id := 'none', @lev := 10) var_init_subquery
     ORDER BY guid
) sq
GROUP BY guid

大多数情况下,在MySQL中使用会话变量时会出现错误,原因是语法错误且未经测试。在下面的查询中,我们使用两个会话变量。第一个@current_level存储当前级别值的滞后。guid\u no变量存储了guid\u no的延迟。因此,计算匹配行的逻辑是,我们遇到非200值中的200值,并且guid\u no没有更改。然后,可以通过guid\u no对其进行聚合,以获得所需的最终结果

SET @current_level = NULL;
SET @guid_no = NULL;

SELECT
    t.guid,
    SUM(guid_sum) AS times
FROM
(
    SELECT
        CASE WHEN (@current_level <> 200 AND current_level = 200) AND (@guid_no = guid)
             THEN 1 END AS guid_sum,
        @current_level:=current_level,
        @guid_no:=guid AS guid,
        current_value,
        pk
    FROM sensor_logs
    ORDER BY guid, pk
) t
GROUP BY t.guid;
输出:

此处演示:

顺便说一句,如果您在MySQL中使用会话变量来模拟行号功能时遇到困难,下面是一个非常有用的参考资料:


您已经提到性能是您关心的问题,随着记录数量的增加,尝试任何类型的select查询都同样需要时间

在我看来,一种方法是

在表上为后插入创建触发器 根据NEW.guid,查找最后一条记录并查看它是否为200 仅使用guid和计数维护一个单独的表 当最后一条记录不是200时,更新guid的新表计数,如果没有记录,则插入 我觉得总体而言,这将优化性能

附加索引GUID、pk以获得更高的速度。参见MariaDB 10.2中的解释选择…行编号;请参见窗口功能