2014-02-28 61 views
2

我想写一个MYSQL查询从一个表的多行组装数据,然后将该结果与另一个表结合。我正在使用GROUP_CONCAT将4+行转换为一个字符串,并且该部分在某种程度上起作用,但不是我想要的方式。MYSQL与group_concat和不相等的键嵌套/相关子查询

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

我已经安装了一些数据的两个样品的表和在sqlfiddle here

一个查询我会分手问题分为两个部分:

  1. 我有从键盘捕获击键的表。用户可以在键盘上执行多项操作,包括输入安全代码以解锁功能。安全码可以是4或5位数字,并且总是以*结尾。一个4位数的代码会在数据库中产生5个独特的记录 - 4个代码和1个*。我必须以相反的顺序阅读这些代码才能读取输入的代码。我写下面的查询为我建立这些代码。
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)第一和可能的明显的问题是,在查询时间(la.skey运行只是罚款1个条件 - > 92956以上的查询)。我似乎无法一次运行整个表。上面查询中的数字92956是用户按下的时间的数字表示*一个特定场合,并且在此之前我正在读取几条记录以组装代码字符串。理想情况下,我想获得用户按下的所有skey2的列表*,其中有相应的代码

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

  1. 一旦第一个问题已经解决,我想加入与另一台,使得我表示从第二个表中的所有记录,只是将代码从第一个表(如上)的输出输出。这两个表格通过skey2相关联 - 用户按下*的时间生成图像,并在另一个表格中捕获这些图像的细节。我希望一旦第一个查询可用,我就可以做一个简单的连接。如果这是不可能的或者会以另一种方式更好地完成,请告知。

数据库和所有相关的代码(和这些查询)将在树莓派,所以我可能会受到资源的约束在一定程度上运行,我想办法劝也确保最终查询尽可能高效。预计这两张表每天都会增长100多条记录。

我试图在最后几天查询的许多组合 - 嵌套,相关和两​​者的组合。我认为这更复杂,因为我正在查找的代码必须使用它们之间没有真正链接的记录进行汇编(skey2值在一个代码的单个记录之间变化)。我也遇到了一个障碍,试图编写一个相关的查询来直接尝试上面问题2中所需的输出,我认为这样做失败了,因为MySQL不会让我有两个深层次的关联。

感谢您阅读迄今为止和我对这篇长文章的道歉。再次,相关表格和数据可在Sqlfiddle here

+0

尽管有办法从表格中获取安全代码,但我认为当表格变大时它们会很贵。我不禁想到合并键击应该由代码从键盘获取事件并将其存储到数据库中,而不是存储每个原始键击。 – Barmar

+0

欢迎来到SO。感谢仔细思考的问题。它看起来像有一个按键的时间序列,而且不同的代码以大于几秒的时间分隔。 –

+0

@Barmar:这几乎花了我几个月的时间才能得到原始的按键!这些按键发送的小键盘的自动化能力非常有限,所以我的选择是通过数据库处理这些数据,或者构建一个Web服务,在通往数据库的途中陷入并构建代码。如果目前的方法长期存在任何问题,我将调查Web服务的可能性。感谢您的回应。 – jinxjy

回答

1

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

此查询(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:[email protected]+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:[email protected]+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:[email protected][email protected], 
      @seq:[email protected][email protected]) 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 | 

有趣的问题!

+0

哇。这是一个惊人的解决方案。感谢您花时间解释答案。我能够在网上使用你的解释和一些MySQL参考资料来解决这个难题的最后一点;上面最后一个查询中的最后两个序列是有效的用户输入,它仍然需要分解为两个单独的代码,'*'作为分隔符。我已经用你的查询的修改版本[这里](http://sqlfiddle.com/#!2/6ffed/53/0)做到了。我将单独发布最终查询作为答案,以便未来的读者更容易。 – jinxjy