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

时间:2017-06-11 23:39:29

标签: mysql sql query-optimization

原文:Counting non-contiguous values

我会稍微改变一下这个结构,这样我就可以做的更明显了。

鉴于:

+------+---------------+---------------+----+
| 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)的内部子查询往往花费不到一秒钟,但整个查询表现不佳(如果有人可以解释为什么会这样,我会很感激,可能是因为它的大尺寸临时表?)

问题是在给定表格大小的情况下完成我正在尝试的内容的有效方法。

旧查询参考:

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

2 个答案:

答案 0 :(得分:1)

您已经提到性能是您关注的问题,尝试任何类型的选择查询同样需要时间,因为记录数量会增加。

在我看来,方法是

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

答案 1 :(得分:0)

大多数情况下,由于语法错误和未经测试,在MySQL中使用会话变量时会出错。在下面的查询中,我们使用两个会话变量。第一个guid_no存储当前级别值的滞后。 guid_no变量存储了200的延迟。然后,计算匹配行的逻辑是,我们遇到非200guid_no值, guid_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; 可以通过聚合来获得您想要的最终结果。

save_path='/home/imagefolder'
for i in range(1,11):
    name=i
fullname=os.path.join(save_path,str(name)+".png")

<强>输出:

enter image description here

在这里演示:

Rextester

顺便说一句,如果您在MySQL中使用会话变量来模拟行号功能,那么这是一个很好的参考,我觉得非常有帮助:

http://www.mysqltutorial.org/mysql-row_number/