2014-02-28 62 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