如何在 sqlite 表中找到间隙?

Posted

技术标签:

【中文标题】如何在 sqlite 表中找到间隙?【英文标题】:How do I find gap in sqlite table? 【发布时间】:2015-07-23 14:08:02 【问题描述】:

我有一个以毫秒为单位的时间戳作为主键的 sqlite 表,每行应该彼此相隔 1 秒或 1000 秒。有时我的数据记录器会熄灭,而表中没有当时的数据。如何使用 SQL 语句找到差距?我知道基于光标的解决方案是可能的。

table = PVT
TS
1119636081000
1119636082000
1119636083000
1119636084000
1119636085000
------gap------
1119636090000
1119636091000

【问题讨论】:

您可以制作当前行和上一行之间的增量表,并查找大于 1000 的值 【参考方案1】:

这可能有效。假设表名是“tstamps”,

select a.ts
from tstamps a
where not exists
   (select b.ts
    from tstamps b
    where b.ts = a.ts+1000)
and exists
   (select c.ts
    from tstamps c
    where c.ts = a.ts+2000)

另一种方式

select a.ts
from tstamps a
where not exists
   (select b.ts
    from tstamps b
    where b.ts = a.ts+1000)
and a.ts <
   (select max(c.ts)
    from tstamps c
   )

使用减号运算符。我不确定,这些查询中哪一个的性能更好。

select ts+1000
from pvt
where ts != (select max(ts) from pvt)
minus
select ts
from pvt
where ts != (select min(ts) from pvt)

【讨论】:

最后一个使用 MINUS 对我有用,我想,除了 sqlite 没有减号运算符,所以我使用了 except。我使用 select ts from pvt where ts between X 和 Y 检查了其中一些,我看到了差距。性能并不是一个真正的问题,因为我不会经常运行此查询,但它在 180 万行上花费了 9 秒。【参考方案2】:

类似这样(假设 PVT.TS 是您的列名):

SELECT * FROM 'table' WHERE PVT.TS ISNULL;

SELECT * FROM 'table' WHERE PVT.TS IS NULL;

如果您的收藏家实际上输入了一个空白条目,您可能需要

WHERE PVT.TS = ''

where ifnull(some_column, '') = ''

【讨论】:

如果有间隙,则不创建行,PVT为表名,TS为列。 啊,所以它不是一个空字段,而是模式的中断。我认为它们并不总是每次都增加 5000?【参考方案3】:

在撰写本文时,SQLite 不支持像 LAG(TS) OVER (ORDER BY TS ASC)LEAD() OVER 这样的窗口函数,它们很容易分别为您提供前面和后面的 TS 值。

所以,你需要自己做:

sqlite> .mode col
sqlite> .width 14 14 14
sqlite>    SELECT PVT.TS  AS measurement,
                  prev.TS AS prev,
                  next.TS AS next
             FROM PVT
        LEFT JOIN PVT next ON PVT.TS = (next.TS - 1000)
        LEFT JOIN PVT prev ON PVT.TS = (prev.TS + 1000);

这会给你这样的东西(我使用了不同的数据,你会看到):

-- measurement        prev            next     
  -------------   -------------   -------------
  1119636081000                   1119636082000   -- gap (no previous at all)
  1119636082000   1119636081000   1119636083000 
  1119636083000   1119636082000   1119636084000 
  1119636084000   1119636083000   1119636085000 
  1119636085000   1119636084000                   -- gap (no next offset 1000)
  1119636088000                   1119636089000   -- gap (no previous offset 1000)
  1119636089000   1119636088000                   -- gap (no next at all)

您也可以始终将该查询限制为仅那些记录 WHERE prev.TS IS NULL OR next.TS is NULL

【讨论】:

这个查询在将近 200 万行上花费的时间太长了。【参考方案4】:

创建一个至少有 86400 行的计数表(一天一秒):

create table Tally(n integer primary key not null);
insert into Tally(n) values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
insert into Tally(n) select null from tally n1 , tally n2, tally n3, tally n4, tally n5;

将您的 PVT 表加入当天的转置记录:

select 1119636081000 + tally.n*1000 as Expected, pvt.ts from tally left join pvt on pvt.ts = 1119636081000 + tally.n*1000 where tally.n <= 86400 limit 15;

给定一个我用您的示例数据填充的表格,我将其作为输出:

Expected       TS
-------------  -------------
1119636081000  1119636081000
1119636082000  1119636082000
1119636083000  1119636083000
1119636084000  1119636084000
1119636085000  1119636085000
1119636086000
1119636087000
1119636088000
1119636089000
1119636090000  1119636090000
1119636091000  1119636091000
1119636092000
1119636093000
1119636094000
1119636095000

如果你在 PVT.TS 为空的地方额外过滤,你应该得到缺失的值:

select 1119636081000 + tally.n*1000 as Expected, pvt.ts from tally left join pvt on pvt.ts = 1119636081000 + tally.n*1000 where tally.n <= 86400 and PVT.ts is null limit 15;

Expected       TS
-------------  ----------
1119636086000
1119636087000
1119636088000
1119636089000
1119636092000
1119636093000
1119636094000
1119636095000
1119636096000
1119636097000
1119636098000
1119636099000
1119636100000
1119636101000
1119636102000

注意:我使用限制 15 来让自己在控制台上保持理智

【讨论】:

您实际上不需要创建计数表。您可以使用临时视图“创建”它,即像这样的递归 CTE:with n as (select 1 x union select x+1 from n where x&lt;50), tally as (select 1119636081000+x*1000 ts from n) select * from tally left join pvt on pvt.ts=tally.ts and pvt.ts is null;(其中 50 和 1119636081000 当然是您需要根据自己的情况调整的数字)

以上是关于如何在 sqlite 表中找到间隙?的主要内容,如果未能解决你的问题,请参考以下文章

SQLite如何从分隔列文本中对另一个表中的值求和?

如何在 MySQL 记录集中找到 ID 间隙?

如何找到 SQLite 中变量定义的指数的值 10 [重复]

如何使用 SQL 找到具有预定义最小间隙大小的所有“间隙”?

如何在 SQLite 表中插入日期和查询日期

如何在 SQLite 中向表中添加变量?