2016-05-23 62 views
3

我正在使用SQLite數據庫,該數據庫定期從多個來源接收大數據轉儲。不幸的是,這些消息來源對於他們轉儲的內容並不知情,而且我最終得到了一次又一次的大量重複記錄。我正在尋找一種方法來刪除這些重複的記錄,而不會影響從過去的轉儲合法更改爲該記錄的記錄。刪除最新的冗餘行並更新時間戳

這裏的數據的一般結構(_id是主鍵):

| _id | _dateUpdated | _dateEffective | _dateExpired | name | status | location | 
|-----|--------------|----------------|--------------|------|--------|----------| 
| 1 | 2016-05-01 | 2016-05-01 |  NULL  | Fred | Online | USA  | 
| 2 | 2016-05-01 | 2016-05-01 |  NULL  | Jim | Online | USA  | 
| 3 | 2016-05-08 | 2016-05-08 |  NULL  | Fred | Offline| USA  | 
| 4 | 2016-05-08 | 2016-05-08 |  NULL  | Jim | Online | USA  | 
| 5 | 2016-05-15 | 2016-05-15 |  NULL  | Fred | Offline| USA  | 
| 6 | 2016-05-15 | 2016-05-15 |  NULL  | Jim | Online | USA  | 

我希望能夠將此數據減少到這樣的事情:

| _id | _dateUpdated | _dateEffective | _dateExpired | name | status | location | 
|-----|--------------|----------------|--------------|------|--------|----------| 
| 1 | 2016-05-01 | 2016-05-01 | 2016-05-07 | Fred | Online | USA  | 
| 2 | 2016-05-15 | 2016-05-01 |  NULL  | Jim | Online | USA  | 
| 3 | 2016-05-15 | 2016-05-08 |  NULL  | Fred | Offline| USA  | 

的這裏的想法是,除了時間戳(我需要通過所有三個字段比較 - 名稱,狀態,位置),第4,5和6行完全重複第2行和第3行。但是,第3行不重複第1行(狀態從聯機更改爲脫機),因此_dateExpired字段在第1行中設置,並且第3行成爲最近的記錄。

我查詢這個表是這樣的:

SELECT * FROM Data WHERE 
    date(_dateEffective) <= date("now") 
    AND (_dateExpired IS NULL OR date(_dateExpired) > date("now")) 

是這種減少可能在SQLite的?

我一般還是SQL和數據庫設計的初學者,所以有可能我沒有以最好的方式構建數據庫。我也樂於接受建議......我希望能夠在給定的時間點查詢數據 - 例如,「Jim在2016-05-06周圍的狀態是什麼?」

在此先感謝!

+0

因此,每個轉儲你想要刪除過去的記錄或保持增長的記錄列表? – Parfait

+0

我想保留越來越多的歷史數據記錄,但我只想保留新記錄,如果它們與舊數據不同的話。如果轉儲添加了與我已有的記錄相同的其他記錄,則我不關心它們。 –

回答

1

考慮使用臨時表將轉儲文件放入DumpTable(每次轉儲前定期清理掉),然後將查詢遷移到您的最終表。

現在,SELECT部分維護一個相關的子查詢(爲所需行計算新的[_dateExpired])和派生表子查詢(根據您的標準過濾非dups)。最後,帶有FinalTable的LEFT JOIN...NULL將確保沒有附加重複記錄,假設[_id]是唯一標識符。下面是例行:

  1. 清理DumpTable

    DELETE FROM DumpTable; 
    
  2. 運行dump程序將被添加到DumpTable

  3. 記錄追加到FinalTable

    INSERT INTO FinalTable ([_id], [_dateUpdated], [_dateEffective], [_dateExpired], 
             [name], status, location) 
    SELECT d.[_id], d.[_dateUpdated], d.[_dateEffective],  
         (SELECT Min(date(sub.[_dateEffective], '-1 day')) 
          FROM DumpTable sub 
         WHERE sub.[name] = DumpTable.[name] 
          AND sub.[_dateEffective] > DumpTable.[_dateEffective] 
          AND sub.status <> DumpTable.status) As calcExpired 
         d.name, d.status, d.location 
    FROM DumpTable d  
    
    INNER JOIN 
          (SELECT Min(DumpTable.[_id]) AS min_id, 
            DumpTable.name, DumpTable.status 
          FROM DumpTable 
         GROUP BY DumpTable.name, DumpTable.status) AS c  
    ON (c.name = d.name) 
    AND (c.min_id = d.[_id]) 
    AND (c.status = d.status) 
    
    LEFT JOIN FinalTable f 
        ON d.[_id] = f.[_id] 
    WHERE f.[_id] IS NULL; 
    
    -- INSERTED RECORDS: 
    -- _id _dateUpdated _dateEffective _dateExpired name status  location 
    -- 1 2016-05-01  2016-05-01  2016-05-07  Fred Online  USA 
    -- 2 2016-05-01  2016-05-01      Jim  Online  USA 
    -- 3 2016-05-08  2016-05-08      Fred Offline USA 
    
+0

謝謝!我沒有想過使用單獨的桌子,但這使得生活變得更容易。 –

0

在SQLite中這種減少可能嗎?

SQL中任何「減少」問題的答案總是是。訣竅是找出你正在減少的軸。

這是一個部分解決方案來說明;它爲每個名稱&位置提供第一個在線日期。

select min(_dateEffective) as start_date 
    , name 
    , location 
from Data 
where status = 'Online' 
group by 
     name 
    , location 

隨着外部聯接回到談判桌前(上名&位置),其中狀態是「脫機」和_dateEffective大於start_date,你會得到你的_dateExpired

_id是主鍵

有每個表需要某種順序「ID」號碼作爲主鍵的普遍持有的誤解。您真正關心的密鑰被稱爲天然密鑰,數據中的1個或多個列可唯一標識數據。在你的情況下,它看起來像那個_dateEffective, name, status, and location。至少,宣佈他們unique以防止意外重複。

+0

感謝您的幫助。我以前從來沒有聽說過天然的鑰匙,聽起來像是我需要研究的東西。有一個數字ID字段的缺點,除了每行存儲一個額外的數字的成本? –