2013-06-02 37 views
1

我有一些代碼來計算同時登錄到應用程序的最大用戶數量。登錄表的結構如下:根據結果條件在mysql查詢中插入或省略條目

idLoginLog | username |  Time   | Type | 
-------------------------------------------------------- 
    1  | pauljones | 2013-01-01 01:00:00 | 1 | 
    2  | mattblack | 2013-01-01 01:00:32 | 1 | 
    3  | jackblack | 2013-01-01 01:01:07 | 1 | 
    4  | mattblack | 2013-01-01 01:02:03 | 0 | 
    5  | pauljones | 2013-01-01 01:04:27 | 0 | 
    6  | sallycarr | 2013-01-01 01:06:49 | 1 | 

找出最大的用戶曾經同時登錄的代碼如下(有對付誰不明確註銷,即如果應用程序是用戶部分殺死不正確退出): calculate most users ever online with MySQL

SET @logged := 0; 
SET @max := 0; 

SELECT 
    idLoginLog, type, time, 
    (@logged := @logged + IF(type, 1, -1)) AS logged_users, 
    (@max := GREATEST(@max, @logged)) AS max_users 
FROM (-- Select from union of logs and records added for users not explicitely logged-out 
    SELECT * from logs 
    UNION 
    SELECT 0 AS idLoginnLog, l1.username, ADDTIME(l1.time, '0:30:0') AS time, 0 AS type 
    FROM -- Join condition matches log-out records in l2 matching a log-in record in l1 
    logs AS l1 
    LEFT JOIN logs AS l2 
    ON (l1.username=l2.username AND l2.type=0 AND l2.time BETWEEN l1.time AND ADDTIME(l1.time, '0:30:0')) 
    WHERE 
    l1.type=1 
    AND l2.idLoginLog IS NULL -- This leaves only records which do not have a matching log-out record 
) AS extended_logs 
ORDER BY time; 

SELECT @max AS max_users_ever; 

http://sqlfiddle.com/#!2/9a114/34

上面的代碼是在以下堆棧溢出問題來達到的

現在有一個問題,登錄條目有時在用戶登錄時沒有寫入表中,因此只有登出條目。這完全混淆了計算。如何更新查詢以忽略沒有事先「登錄」條目的條目?或者,如何在任何單獨的「註銷」條目前2分鐘添加「登錄」條目,以便上述代碼可以達到更合理的結果?

+0

對不起,我知道這不會幫助你解決你的問題,但我認爲這種計算最好由你的應用程序處理。 –

+0

確實,隨着條件變得越來越複雜,使用單個查詢對維護不利。 – Mifeet

+0

只是爲了排除簡單的解決方案 - 只是刪除註銷條目而沒有匹配選項?或插入相關的登錄條目?或者你願意按照Olivier的建議在你的應用程序中處理它? – Mifeet

回答

0

如果您需要檢測哪些記錄表示登錄而沒有匹配的登出記錄,反之亦然,那麼使用唯一的會話ID擴展您的表將會很有幫助。 添加一列session_id,在登錄時生成其值,在會話中記住它並將相同的值輸入到登錄錯誤的session_id。它會簡化查詢。


如果你需要一個查詢,將添加缺少的用戶登錄信息,請嘗試以下操作:

SELECT 0 AS idLoginnLog, l1.username, ADDTIME(l1.time, '-0:30:0') AS time, 1 AS type 
FROM logs AS l1 
    LEFT JOIN logs AS l2 
    ON (l1.username=l2.username AND l2.type=1 AND l2.time BETWEEN ADDTIME(l1.time, '-0:30:0') AND l1.time) 
WHERE 
    l1.type=0 
    AND l2.idLoginLog IS NULL 

Fiddle。)你可以插入結果表(INSERT INTO logs (...) SELECT ...)或添加查詢到原始查詢中的UNION。


正如@OlivierCoilland指出,查詢變得相當複雜,你可以考慮在應用程序端分析。因爲我猜測日誌表非常大,你不應該依賴你適應內存中的所有條目。您可能需要某種「滑動窗口」技術。


第四個選項是刪除不匹配的註銷記錄。我的解決方案需要一個臨時表,所以我不在這裏粘貼整個(很長)的代碼,只能看到fiddle

+0

關於你的session_id建議,你的意思是我可以通過搜索沒有出現在2行中的session_id來找到所有不好的會話?如果是這樣,你能提供這個查詢嗎? – pedromillers

+0

如果我將其改爲30秒而不是幾分鐘,其他建議似乎不起作用,您知道爲什麼嗎? – pedromillers

+0

因爲示例數據中的pauljones例如有4分鐘的登錄和註銷記錄。如果您將其設置爲30秒,那麼您插入一個新的登錄記錄,但舊的登錄記錄將無法匹配。你不應該使用少於你的會話超時。 – Mifeet