我正在使用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;
提前致谢!
答案 0 :(得分:1)
考虑使用转储文件进入DumpTable的转储表(在每次转储之前定期清除),然后INSERT...SELECT
查询迁移到最终表。
现在SELECT
部分维护一个相关子查询(为所需行计算新[_dateExpired]
)和派生表子查询(根据您的标准过滤掉非重复)。最后,带有FinalTable的LEFT JOIN...NULL
是为了确保不附加重复记录,假设[_id]
是唯一标识符。以下是例程:
清理DumpTable
DELETE FROM DumpTable;
运行Dump Routine以附加到DumpTable
将记录附加到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
以防止意外重复。