2016-05-23 68 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字段的缺点,除了每行存储一个额外的数字的成本? –