Sqlite3查找行值中的更改

时间:2019-12-28 20:45:22

标签: python-3.x database pandas sqlite triggers

我正在使用Python将如下所示的海洋AIS数据收集到sqlite3数据库中;

|MMSI|TSTAMP|LATITUDE|LONGITUDE|COG|SOG|HEADING|NAVSTAT|IMO|NAME|CALLSIGN|TYPE|A|B|C|D|DRAUGHT|DEST|ETA|MGRS|UNIQUEREF|
636018508|1576547278|36.48892|14.52537|286.0|6.2|284|5|9481960|BRIGHT STAR|D5QA2|79.0|149.0|31.0|9.0|21.0|9.8|DZGHZ|12-19 11:00|33SVA57483828|636018508-1576547278.0
636018508|1576547158|36.48695|14.5334|286.0|12.1|284|0|9481960|BRIGHT STAR|D5QA2|79.0|149.0|31.0|9.0|21.0|9.8|DZGHZ|12-19 11:00|33SVA58203806|636018508-1576547158.0
636018508|1576547098|36.486|14.53742|286.0|12.1|284|0|9481960|BRIGHT STAR|D5QA2|79.0|149.0|31.0|9.0|21.0|9.8|DZGHZ|12-19 11:00|33SVA58563795|636018508-1576547098.0
636018508|1576546858|36.48185|14.55417|284.0|12.2|284|0|9481960|BRIGHT STAR|D5QA2|79.0|149.0|31.0|9.0|21.0|9.8|DZGHZ|12-19 11:00|33SVA60063748|636018508-1576546858.0
636018508|1576545900|36.46573|14.6182|289.0|12.2|288|0|9481960|BRIGHT STAR|D5QA2|79.0|149.0|31.0|9.0|21.0|9.8|DZGHZ|12-19 11:00|33SVA65793567|636018508-1576545900.0

每分钟由熊猫将python插入到临时表中的python下载数据。为了避免重复的行,最后一个字段是将前两个字段(DateTime时期和唯一ID)串联在一起的组合,为每个行赋予唯一的ID。然后在我的主表中,然后设置一个具有唯一约束的列。

然后使用复制数据;

INSERT OR IGNORE INTO <main table> FROM <temp table>;

我想将所有数据保留在主表中,以便建立历史数据库,但是我还需要监视某些字段中的更改。例如,在数据片段中,您可以看到在顶部行中,第7字段已从12.1更改为6.2,而第8字段已从0更改为5。顶部行是最新更新,因为它具有最新的纪元。问题在于主数据库将非常大(当前有49487034行),我需要在不到最新数据输入之前的1分钟内检测下载的所有新行的任何更改。

到目前为止我已经尝试过了;

GROUP BY unique MMSI ORDER BY TSTAMP DESC LIMIT 2; 

然后使用条件语句检查更改。问题在于这样做的时间很长,因为我们正在过滤整个数据库,然后对所有记录进行排序,然后检查更改。

我不是最有经验的程序员,但是我想说,我需要使用一种更矢量化的方法,而不是本质上是一个循环。

我的第二个想法是建立第三个表,该表也从temp表中获取数据,但是它仅保存最近的2次更新,但是来自数据库POV,我不确定这将如何影响数据库,因为总体而言,也不确定是否有更快的方法来实现我想要的目标。

我的最后一个想法是使用触发器,但是肯定会和上面的第二个想法一样快吗?

正在寻找有关如何在不到1分钟的时间内识别出这些变化的任何建议?谢谢。

1 个答案:

答案 0 :(得分:1)

您可以使用lag()窗口函数(需要Sqlite 3.25或更高版本)将当前行的值与上一行的值进行比较,并仅选择不同的值。像这样:

WITH cte AS
 (SELECT mmsi, tstamp, sog, navstat
       , lag(sog, 1) OVER previous AS prev_sog
       , lag(navstat, 1) OVER previous AS prev_navstat
  FROM data
  WINDOW previous AS (PARTITION BY mmsi ORDER BY tstamp))
SELECT * FROM cte
WHERE sog <> prev_sog OR navstat <> prev_navstat
ORDER BY mmsi, tstamp;

db-fiddle example

如果要将其限制为仅带有最后一刻的时间戳记的行,则可以使用strftime()添加where子句:

WITH cte AS
 (SELECT mmsi, tstamp, sog, navstat
       , lag(sog, 1) OVER previous AS prev_sog
       , lag(navstat, 1) OVER previous AS prev_navstat
  FROM data
  WHERE tstamp >= strftime('%s', 'now', '-1 minute')
  WINDOW previous AS (PARTITION BY mmsi ORDER BY tstamp))
SELECT * FROM cte
WHERE sog <> prev_sog OR navstat <> prev_navstat
ORDER BY mmsi, tstamp;