2012-01-11 47 views
2

我有用戶操作表,每個用戶操作都有一個用戶關聯,一個類型和一個時間戳。這裏有一個簡化的例子:匹配來自MySQL表的用戶操作:每個CLOSE應該與給定用戶的前一個OPEN匹配

TABLE USER_ACTIONS 
------------------------ 
USER | TYPE | TIMESTAMP 
------------------------ 
a | OPEN | 0 
b | OPEN | 1 
a | CLOSE | 2 
a | OPEN | 3 
b | CLOSE | 4 
a | CLOSE | 4 
a | OPEN | 5 <-- "orphaned" OPEN, with no corresponding CLOSE. Should be ignored. 
c | OPEN | 3 
c | CLOSE | 5 
a | OPEN | 6 
a | CLOSE | 8 

我想獲得一個交易時間列表。對於特定用戶,每個CLOSE應與之前的OPEN匹配。

我想會是這個樣子的結果:

USER | TRANSACTION_TIME 
----------------------- 
a | 2 
b | 3 
a | 1 
c | 2 
a | 2 

我不關心排序。

我知道這可以通過編程來完成,但是有可能使用一些聰明的SQL嗎?

UPDATE:

以編程方式做到這一點,一般的想法是......

  1. 選擇所有的 「CLOSE」 行動,通過TIMESTAMP降序排列的。
  2. 對於列表中的每個人,嘗試查找由同一用戶進行的前一個「OPEN」操作。將TIMESTAMP限制在「CLOSE」動作TIMESTAMP之前,按TIMESTAMP DESC對結果進行排序,並將其限制爲1.
  3. 對於該對,計算時間差,並輸出結果。

下面是一些僞代碼,但實際上我想SQL,這是否巧妙:

for each CLOSE_ACTION IN ("SELECT USER, TYPE, TIMESTAMP FROM USER_ACTIONS WHERE TYPE='CLOSE' ORDER BY TIMESTAMP DESC;") { 
    OPEN_ACTION = "SELECT USER, TYPE, TIMESTAMP FROM USER_ACTIONS 
        WHERE TYPE='OPEN' 
        AND USER='<CLOSE_ACTION.USER>' 
        AND TIMESTAMP='<CLOSE_ACTION.TIMESTAMP>' 
        ORDER BY TIMESTAMP DESC 
        LIMIT 1"; 
    if OPEN_ACTION != empty/null then { 
     print CLOSE_ACTION.USER, CLOSE_ACTION.TIMESTAMP - OPEN_ACTION.TIMESTAMP; 
    } 
} 
+0

你知不知道你*總是*有一個OPEN關聯關聯?或者用戶可以打開,打開,關閉,打開,打開,關閉,關閉等等?如果數據不是*完全清潔*,你知道你想如何處理這種情況嗎? *(忽略重複打開,但將'CLOSE,CLOSE'視爲'CLOSE,OPEN,CLOSE',例如?)* – MatBailie 2012-01-11 16:24:56

+0

好問題:不幸的是,每個OPEN都沒有CLOSE。我會更新我的表來反映這一點。對於這些情況,我只想讓孤兒的OPEN被忽略,而不是被計算在內。 – 2012-01-11 16:31:02

+0

在你的例子中,如果模式是'OPEN,CLOSE,CLOSE',那麼應該忽略哪個關閉?我的答案目前忽略了第二個「CLOSE」,並符合你的描述的其餘部分。 – MatBailie 2012-01-11 16:49:19

回答

1

當且僅當前面的事件是OPEN時,這需要每個CLOSE事件並將其匹配到前面的事件。

SELECT 
    OPEN.user, 
    OPEN.transaction_time 
    CLOSE.transaction_time 
FROM 
    user_actions as CLOSE 
INNER JOIN 
    user_actions as OPEN 
    ON OPEN.user = CLOSE.user 
    AND OPEN.transaction_time = (SELECT MAX(transaction_time) FROM user_action 
           WHERE user = CLOSE.user 
           AND transaction_time < CLOSE.transaction_time 
           AND type='OPEN') 
WHERE 
    CLOSE.type = 'CLOSE' 
+0

這看起來非常非常接近,但我無法完成它的工作。我會繼續擺弄它。我可以編輯你的答案,一旦我得到它的工作。 – 2012-01-11 16:58:57

+0

繁榮。作品!謝謝! – 2012-01-11 17:02:15

+0

請注意,編輯完成後,這會改變「OPEN,CLOSE,CLOSE」情況下的行爲,現在使用兩個關閉引用,並且兩者回看相同的OPEN。 – MatBailie 2012-01-11 18:00:10

0

嘗試:

select user, 
     timestamp, 
     (select min(timestamp) 
     from user_actions u 
     where a.user = u.user and 
       u.type = 'CLOSE' and 
       u.timestamp > a.timestamp) - timestamp 
from user_actions a 
where type = 'OPEN' 

(假設總是會有一個匹配接近每個打開)

+0

'和u.timestamp> a.timestamp'? – MatBailie 2012-01-11 16:46:50

+0

@Dems:d'oh!謝謝! – 2012-01-11 17:07:11

0

下面的查詢適用於提出的問題。我已經在我的本地機器上執行它,它似乎工作正常。

select u1.user_name ,u2.timestamp- max(u1.timestamp) difference 
from user_actions u1,user_actions u2 
where u1.type = 'OPEN' and 
u2.type = 'CLOSE' and 
u1.timestamp <u2.timestamp and u1.user_name = u2.user_name 
group by (u1.user_name , u2.timestamp); 
相關問題