2013-01-03 67 views
4

我有一個用於記錄實驗室訪問數據的表。表結構是這樣的:如何檢查SQL表中的數據完整性?

create table accesslog 
(
    userid int not null, 
    direction int not null, 
    accesstime datetime not null 
); 

本實驗只有一個門是在訪問控制下。因此用戶必須先「進入」實驗室才能「離開」。在我最初的設計中,我將「方向」字段設置爲1(用於輸入實驗室)或-1(用於離開實驗室)的標誌。所以,我可以使用查詢:

SELECT SUM(direction) FROM accesslog; 

拿到實驗室內的用戶總數。理論上,它起作用了;因爲對於任何給定的userid,「方向」總是處於1 => -1 => 1 => -1的模式。

但是很快我發現日誌消息會在從實驗室門到服務器的傳輸路徑中丟失,或者由繁忙的網絡或硬件故障丟失。當然,我可以強制使用序列號,ACK,重傳,硬件冗餘等傳輸路徑,但最終我還是會得到這樣的:

userid direction accesstime 
------------------------------------- 
1   1   2013/01/03 08:30 
1  -1   2013/01/03 09:20 
1   1   2013/01/03 10:10 
1  -1   2013/01/03 10:50 
1  -1   2013/01/03 13:40 
1   1   2013/01/03 18:00 

這是一個最新的日誌爲用戶「1」 。很顯然,我在10點50分到13點40分之間爲該用戶輸入實驗室失去了一條日誌消息。當我查詢這些數據時,他仍然在實驗室,所以2013/01/03 18:00之後沒有退出日誌;這是肯定的。

我的問題是:有什麼辦法可以用SQL命令「查找」這個數據不一致嗎?我的系統中共有5000名用戶,實驗室24小時運行,實驗室沒有這樣的「魔法時間」。如果我必須編寫代碼逐行檢查「方向」字段的連續性,那麼我會非常可怕。

我知道用正確的數據「修復」日誌是不可能的。我只想知道「哦,我對userid = 1有數據不一致問題」,以便我可以將標記的修正數據添加到正確的最終統計數據中。

任何意見,將不勝感激,即使改變表結構也沒關係。

謝謝。

編輯:對不起,我沒有提到的細節。

當前我正在使用混合SQL解決方案。上面顯示的表格是MySQL,它僅包含24小時內的日誌作爲快速瀏覽的「實時」狀態。

每天早上3點,將會啓動一個用C++在POSIX上編寫的預定進程。此過程將計算統計數據,並通過專有協議TCP套接字將日常統計信息添加到Oracle數據庫中,然後它將從MySQL中刪除舊數據。

Oracle部分不是由我處理的,我無能爲力。我只是想確保每天的最終統計數據是正確的。

數據大小約爲每天200,000條記錄 - 我知道這聽起來很瘋狂,但它是真實的。

+1

您正在使用哪種RDBMS? – Bridge

+0

問題在於'消息'沒有傳遞給數據庫服務器。使用保證傳遞的消息代理(MSMQ,如果你進入微軟的話) –

+0

這很難用SQL,但是在代碼中是微不足道的。你的數據量是多少?你用什麼語言來驅動SQL? –

回答

0

好吧我想通了。感謝a_horse_with_no_name提供的想法。

我的最終解決方案是這樣的查詢:

SELECT userid, COUNT(*), SUM(direction * rule) FROM (
    SELECT userid, direction, @inout := @inout * -1 AS rule 
    FROM accesslog l, (SELECT @inout := -1) r 
    ORDER by userid, accesstime 
) g GROUP by userid; 

首先我創建@inout的圖案將產生1 => -1 => 1 => -1中的 「規則」 列中的每一行。比我計算乘法乘積比較方向字段和規則列。

即使某些用戶有奇數記錄,也可以;因爲每個用戶應該遵循與「規則」相同或顛倒的模式。所以乘積的總和應該等於COUNT()或-1 * COUNT()。

通過檢查SUM()和COUNT(),我可以確切知道哪個userid出錯了。

2

您沒有聲明您的DBMS,所以這是ANSI SQL(適用於大多數現代DBMS)。

select userid, 
     direction, 
     accesstime, 
     case 
     when lag(direction) over (partition by userid order by accesstime) = direction then 'wrong' 
     else 'correct' 
     end as status 
from accesslog 
where userid = 1 

對於訪問日誌中的每一行,您將獲得一列「狀態」,指示該行是否「打破」了該規則。

可以過濾掉那些使用是無效的:

select * 
from (
    select userid, 
     direction, 
     accesstime, 
     case 
      when lag(direction) over (partition by userid order by accesstime) = direction then 'wrong' 
      else 'correct' 
     end as status 
    from accesslog 
    where userid = 1 
) t 
where status = 'wrong' 

我不認爲有一種方法使用數據庫中的約束條件(雖然我的感覺來執行這樣的規則是PostgreSQL的排除約束條件可能幫助這裏)

+0

好主意。我可能能夠從此跟進。 – RichardLiu

1

爲什麼不使用SUM()與WHERE字段來篩選USER。

如果你得到0或1以外的任何東西,那麼你肯定有問題。

+0

這確實是我的第一反應。但請看我上面的例子:SUM(方向)是0,但仍有數據不一致。使用SUM()可以檢測一些錯誤情況,但不是全部。 – RichardLiu