2008-10-08 113 views
1

我想記錄用戶狀態,然後可以根據我們不斷變化的記錄歷史報告的歷史數據報告。我試圖做到這一點的SQL(使用PostgreSQL),我有記錄,如下面的用戶變化提出的結構。SQL - 狀態機 - 基於變更

CREATE TABLE users (
    userid SERIAL NOT NULL PRIMARY KEY, 
    name VARCHAR(40), 
    status CHAR NOT NULL 
); 

CREATE TABLE status_log (
    logid SERIAL, 
    userid INTEGER NOT NULL REFERENCES users(userid), 
    status CHAR NOT NULL, 
    logcreated TIMESTAMP 
); 

這是我提出的基於數據的表結構。

對狀態欄「A」代表活躍用戶,並且「S」表示已暫停的用戶,

INSERT INTO status_log (userid, status, logcreated) VALUES (1, 's', '2008-01-01'); 
INSERT INTO status_log (userid, status, logcreated) VALUES (1, 'a', '2008-02-01'); 

所以該用戶已被暫停在1月1日和積極的再次2月1。

如果我想獲得於2008年1月15日客戶的暫停名單,然後用戶標識1應該顯示出來。如果我在2008年2月15日獲得暫停的客戶名單,則不應出現用戶ID 1。

1)這是結構的數據爲這種查詢的最佳方式?

2)如何查詢這個結構或者你提出的修改後的結構中的數據,這樣我就可以簡單地得到一個日期(比如說1月15日),並找到那個日期的活動狀態的客戶列表只有SQL?這是SQL的工作嗎?

回答

2

這是可以做到的,但如果你存儲在每個日誌的結束日期將是一個很大更有效率。隨着你的模型,你必須做一些事情,如:

select l1.userid 
from status_log l1 
where l1.status='s' 
and l1.logcreated = (select max(l2.logcreated) 
        from status_log l2 
        where l2.userid = l1.userid 
        and l2.logcreated <= date '2008-02-15' 
        ); 

與其他列它woud更像:(道歉任何語法錯誤,我不知道PostgreSQL系統)

select userid 
from status_log 
where status='s' 
and logcreated <= date '2008-02-15' 
and logsuperseded >= date '2008-02-15'; 

爲了解決由Phil提出的一些其他問題:

的用戶可能會從積極的移動,來暫停,​​取消向,行動再次。這是一個簡化版本,在現實中,還有更多的國家和人民可以直接從一個國家轉移到另一個。

這將出現在像這樣的表:

userid from  to   status 
FRED 2008-01-01 2008-01-31 s 
FRED 2008-02-01 2008-02-07 c 
FRED 2008-02-08   a 

我用一個空當前記錄的「到」日期。我本可以使用像2999-12-31這樣的未來日期,但在某些方面,null更可取。

此外,當前狀態也不會有「結束日期」,所以我認爲這稍微打破了您的查詢?

是的,我的查詢將不得不重新寫爲

select userid 
from status_log 
where status='s' 
and logcreated <= date '2008-02-15' 
and (logsuperseded is null or logsuperseded >= date '2008-02-15'); 

這種設計的缺點是,每當用戶的狀態變化,你必須結束日的當前status_log以及創建新的一個。然而,這並不困難,我認爲查詢優勢可能勝過此。

0

@Tony「結束」日期不一定適用。

用戶可能會從活動,暫停,取消,再次激活。這是一個簡化版本,在現實中,還有更多的國家和人民可以直接從一個國家轉移到另一個。

此外,當前狀態也不會有「結束日期」,所以我認爲這稍微打破了您的查詢?

0

@Phil

我喜歡託尼的解決方案。它似乎最巧妙地模擬了所描述的情況。任何特定的用戶都有一段時間(一分鐘,一小時,一天等)的狀態,但是持續一段時間,而不是一瞬間。既然你想知道誰在一段時間內處於活躍狀態,那麼將信息建模爲持續時間似乎是最好的方法。

我不確定其他狀態是否有問題。如果有人活躍,然後暫停,然後取消,然後再次激活,那麼每個狀態將適用於給定的持續時間,他們不會嗎?這可能是短暫的,例如幾秒鐘或一分鐘,但它們仍然會持續一段時間。

您是否擔心某個人的狀態可能會在給定的一天內多次更改,但您想知道某個特定日期哪些人處於活動狀態?如果是這樣,那麼你只需要更具體地定義在某一天活躍的含義。如果他們在當天的任何一部分都活躍就足夠了,那麼Tony的回答就會很好。如果他們必須在給定的一天內活躍一段時間,那麼可以修改Tony的解決方案以簡單地確定時間長度(以小時,分鐘或天計),並在WHERE子句中添加更多限制在該狀態下檢索適當的日期,狀態和時間長度。

至於當前狀態沒有「結束日期」,只要結束日期可以爲空,那也沒有問題。只需使用像這樣的「WHERE enddate < ='2008-08-15'或enddate爲空」。

1

Postgres支持分析查詢嗎?這會給活躍用戶2008-02-15

select userid 
from 
(
select logid, 
     userid, 
     status, 
     logcreated, 
     max(logcreated) over (partition by userid) max_logcreated_by_user 
from status_log 
where logcreated <= date '2008-02-15' 
) 
where logcreated = max_logcreated_by_user 
    and status  = 'a' 
/
+0

是的,它支持分析查詢 - > http://www.postgresql.org/docs/8.4/static/tutorial-window.html – filiprem 2013-11-20 22:36:43