MYSQL使用group_concat和不等密钥嵌套/关联子查询

时间:2014-02-28 01:34:12

标签: mysql sql

我试图编写一个MYSQL查询来汇总一个表的多行数据,然后将该结果与另一个表连接起来。我使用GROUP_CONCAT将4+行转换为一个字符串,该部分在某种程度上起作用,但不是我想要的方式。

首先帮助可视化问题的背景:我正在为自己的家庭自动化项目工作,并寻找一种方法来关联来自各种来源的传感器输入。我有各种各样的传感器将数据流式传输到不同的mysql表中,我在表中关联数据的主要方法是在每个表中使用时间戳,这些时间戳可能并不总是精确匹配,而且我在使用简单的SQL子句时遇到问题。我现在就一个我几天都无法解决的特殊问题寻求帮助。

我在sqlfiddle here

中设置了两个包含一些数据和一个查询的示例表

我将把问题分成两部分:

  1. 我有一张桌子可以捕捉键盘上的击键。用户可以在键盘上执行多项操作,包括输入安全代码以解锁功能。安全代码可以是4或5位长,并且始终以*结尾。 4位数代码在数据库中产生5个唯一记录 - 代码为4,*为*。我必须以相反的顺序读回这些内容才能读取输入的代码。我写下面的查询为我构建这些代码。
  2. SELECT GROUP_CONCAT(keyvalue ORDER BY id SEPARATOR '') AS code,
           skey2 
    FROM (
        SELECT keyvalue,skey2,skey1,id 
        FROM lockactivity AS la 
        WHERE la.skey2 <= 92956 
        ORDER BY la.id DESC LIMIT 6 ) AS codelist
    WHERE length(codelist.keyvalue)=1 AND codelist.keyvalue<>'*'
    

    到目前为止,这可以处理4/5位数代码。该表可以包含背靠背的代码行,或者在其间具有其他记录。

    我对此查询有两个挑战:

    a)第一个也可能是明显的问题是查询一次只运行1个条件(上面的查询中的la.skey - &gt; 92956)。我似乎不能立刻为整个桌子运行这个。上面查询中的数字92956是用户按下*一个特定场合的时间的数字表示,并且我在那之前读取一些记录来组装代码串。理想情况下,我想获得所有skey2的列表,其中用户按下*并使用相应的代码

    b)也可能有多个传感器同时登录到桌面,因此代码可能会混乱,锁定字段是传感器的唯一标识符。

    1. 一旦解决了第一个问题,我想将该输出与另一个表的输出结合起来,以便显示第二个表中的所有记录以及第一个表中的代码(如上所示)。这两个表由skey2相关 - 用户按*生成图像的时间,其他详细信息在另一个表中捕获。我希望在第一个查询可用后我将能够进行简单的连接。如果这不可能或将以其他方式更好地完成,请提供建议。
    2. 数据库和所有相关代码(以及这些查询)将在覆盆子pi上运行,所以我可能会受到某种程度的资源限制,我正在寻找方法建议也确保最终的查询是尽可能高效。预计这两张表每天都会增加100多条记录。

      我在过去几天尝试了许多查询组合 - 嵌套,相关和两​​者的组合。我认为这更复杂,因为我要查找的代码必须使用它们之间没有真正链接的记录进行汇编(skey2值在一个代码的各个记录之间发生变化)。我也试图写一个相关的查询来直接尝试上面问题2中所需的输出,我认为失败了,因为MySQL不会让我有两个级别的相关性。

      感谢您阅读到目前为止,并为我很长的帖子道歉。同样,相关表格和数据可在Sqlfiddle here

      上获得

1 个答案:

答案 0 :(得分:1)

这是一个时间序列问题。您在此锁定表中有一系列击键(与其他东西混合)。我们需要将击键分成彼此靠近的组。

此查询(http://sqlfiddle.com/#!2/6ffed/21/0)就是这样做的。为了完整起见,我将离开*按键。

SELECT TIMESTAMPDIFF(SECOND, @prevVal, req_time) AS timediff,
       @prevVal := req_time AS req_time,
       lockip, keyvalue
  FROM lockactivity,
       (SELECT @prevVal := MIN(req_time) -
                           INTERVAL 1 HOUR FROM lockactivity) AS r
 WHERE LENGTH(keyvalue) = 1
 ORDER BY lockip, id

结果集的前几行如下所示:

TIMEDIFF     REQ_TIME           LOCKIP         KEYVALUE
3604      2014-02-27 09:29:55   192.168.1.49    3
0         2014-02-27 09:29:55   192.168.1.49    6
1         2014-02-27 09:29:56   192.168.1.49    4
0         2014-02-27 09:29:56   192.168.1.49    1
0         2014-02-27 09:29:56   192.168.1.49    *
1155      2014-02-27 09:49:11   192.168.1.49    3

看看每个新的“一堆”击键是如何从一个相对较大的timediff开始的?

我们的下一步是创建一个方案,在每一组击键上添加一个新的序列号。这个讨厌的查询(http://sqlfiddle.com/#!2/6ffed/25/0)就是这样做的。

SELECT TIMESTAMPDIFF(SECOND, @prevVal, req_time) AS timediff,
       IF(ABS(TIMESTAMPDIFF(SECOND, @prevVal, req_time))> 120,
          @seq:=@seq+1, 
          @seq) AS seq,
       @prevVal := req_time AS req_time,
       lockip, keyvalue
  FROM lockactivity,
       (SELECT @prevVal := MIN(req_time) - 
                           INTERVAL 1 HOUR FROM lockactivity) AS r,
       (SELECT @seq := 0) AS s
 WHERE LENGTH(keyvalue) = 1
 ORDER BY lockip, id

注意上面的120。我随意选择群集击键,延迟时间为120秒或更短。您可能需要选择其他群集编号。另请注意,当我们获得第二个ip时,timediff会向后跳转,因此我使用ABS()作为聚类标准。

最后,我们需要用GROUP_CONCAT等来总结这些东西。这个查询(http://sqlfiddle.com/#!2/6ffed/28/0)可以做到这一点。

SELECT lockip,
       GROUP_CONCAT(keyvalue 
                    ORDER BY id
                    SEPARATOR '') AS keystrokes,
       MAX(req_time) AS finish_time

FROM (       

SELECT TIMESTAMPDIFF(SECOND, @prevVal, req_time) AS timediff,
       IF(ABS(TIMESTAMPDIFF(SECOND, @prevVal, req_time))> 120,
          @seq:=@seq+1, 
          @seq) AS seq,
       @prevVal := req_time AS req_time,
       id, lockip, keyvalue
  FROM lockactivity,
       (SELECT @prevVal := MIN(req_time) - 
                           INTERVAL 1 HOUR FROM lockactivity) AS r,
       (SELECT @seq := 0) AS s
 WHERE LENGTH(keyvalue) = 1
 ORDER BY lockip, id
) AS seq
GROUP BY seq, lockip
ORDER BY MAX(req_time)

这是结果集,似乎正是您所需要的。我不完全确定最后两个关键序列。您可能需要摆弄群集时间才能正确使用。

LOCKIP         KEYSTROKES   FINISH_TIME
192.168.1.49   3641*        2014-02-27 09:29:56
192.168.1.49   3            2014-02-27 09:49:11
192.168.1.49   3641*        2014-02-27 20:29:49
192.168.1.49   3641*        2014-02-27 20:33:32
192.168.1.55   1122*        2014-02-27 21:06:42
192.168.1.55   1122**       2014-02-27 21:45:52
192.168.1.55   1122*        2014-02-27 22:12:38
192.168.1.49   3641*11015*  2014-02-27 22:13:11
192.168.1.49   33015*11015* 2014-02-27 22:20:10

最后,OP对查询进行了一些进一步的调整,以分割出星形分隔的序列。

jinxjy更新:根据我的评论,我对上面的最终查询进行了一些小的更改,除了之前创建的时间戳序列之外,还使用*作为分隔符。其他小编辑包括将120更改为6,因为这是键盘上的标准击键超时并将*隐藏在最终代码列中。这也已发布到sqlfiddle(http://sqlfiddle.com/#!2/6ffed/53/0

 SELECT lockip,
       GROUP_CONCAT(keyvalue 
                    ORDER BY id
                    SEPARATOR '') AS code,
       MAX(req_time) AS finish_time
FROM (
SELECT TIMESTAMPDIFF(SECOND, @prevVal, req_time) AS timediff,
       IF(keyvalue="*",
          @flag:=1,
          @flag:=0) AS flag,       
       IF(ABS(TIMESTAMPDIFF(SECOND, @prevVal, req_time))> 6,
          @seq:=@seq+1+@flag, 
          @seq:=@seq+@flag) AS seq,
       @prevVal := req_time AS req_time,
       id, lockip, keyvalue
  FROM lockactivity,
       (SELECT @prevVal := MIN(req_time) - INTERVAL 1 HOUR FROM lockactivity) AS r,
       (SELECT @seq := 0) AS s,
       (SELECT @flag:= 0) AS n
 WHERE LENGTH(keyvalue) = 1
 ORDER BY lockip, id
) AS seq
WHERE flag=0
GROUP BY seq, lockip
ORDER BY MAX(req_time) DESC

这给了我理想的结果:

|       LOCKIP |  CODE |         FINISH_TIME |
|--------------|-------|---------------------|
| 192.168.1.49 | 11015 | 2014-02-27 22:20:10 |
| 192.168.1.49 | 33015 | 2014-02-27 22:20:04 |
| 192.168.1.49 | 11015 | 2014-02-27 22:13:11 |
| 192.168.1.49 |  3641 | 2014-02-27 22:13:06 |
| 192.168.1.55 |  1122 | 2014-02-27 22:12:38 |
| 192.168.1.55 |  1122 | 2014-02-27 21:45:48 |
| 192.168.1.55 |  1122 | 2014-02-27 21:06:41 |
| 192.168.1.49 |  3641 | 2014-02-27 20:33:32 |
| 192.168.1.49 |  3641 | 2014-02-27 20:29:48 |
| 192.168.1.49 |     3 | 2014-02-27 09:49:11 |
| 192.168.1.49 |  3641 | 2014-02-27 09:29:56 |

有趣的问题!