删除最新的冗余行并更新时间戳

时间:2016-05-23 16:17:17

标签: sql sqlite

我正在使用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行(状态从Online更改为Offline),因此_dateExpired字段在第1行中设置,第3行成为最新记录。

我用这样的方式查询这个表:

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

这种减少在SQLite中是否可行?

我仍然是SQL和数据库设计的初学者,因此我可能无法以最佳方式构建数据库。我也对那里的建议持开放态度......我希望能够在给定的时间点查询数据 - 例如,&#34; Jim在2016年左右的状态是什么?-05-06&#34;

提前致谢!

2 个答案:

答案 0 :(得分:1)

考虑使用转储文件进入DumpTable的转储表(在每次转储之前定期清除),然后INSERT...SELECT查询迁移到最终表。

现在SELECT部分维护一个相关子查询(为所需行计算新[_dateExpired])和派生表子查询(根据您的标准过滤掉非重复)。最后,带有FinalTable的LEFT JOIN...NULL是为了确保不附加重复记录,假设[_id]是唯一标识符。以下是例程:

  1. 清理DumpTable

    DELETE FROM DumpTable;
    
  2. 运行Dump Routine以附加到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
    

答案 1 :(得分:0)

  

这种减少在SQLite中是否可行?

SQL中任何“减少”问题的答案始终是“是”。诀窍是找到你正在减少的轴。

这是一个部分解决方案来说明;它为每个名称提供了第一个在线日期&amp;地点。

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以防止意外重复。