Python 在SQLSELECT查询中,删除第一个和最后0个值,但不在中间

Python 在SQLSELECT查询中,删除第一个和最后0个值,但不在中间,python,sql,sqlite,Python,Sql,Sqlite,这是我的问题陈述。我从一个传感器获得了以下格式的数据: ts |i|p |idx 2019-10-28 06:00:01|0| 0|75522 2019-10-28 06:10:00|0| 0|75522 2019-10-28 06:20:00|0| 0|75522 2019-10-28 06:30:00|0| 0|75522 2019-10-28 06:40:00|0| 0|75522 2019-10-28 06:50:00|0| 0|75522

这是我的问题陈述。我从一个传感器获得了以下格式的数据:

ts                 |i|p  |idx
2019-10-28 06:00:01|0|  0|75522
2019-10-28 06:10:00|0|  0|75522
2019-10-28 06:20:00|0|  0|75522
2019-10-28 06:30:00|0|  0|75522
2019-10-28 06:40:00|0|  0|75522
2019-10-28 06:50:00|0|  0|75522
2019-10-28 07:00:00|0|  0|75522
2019-10-28 07:10:00|0|103|75526
2019-10-28 07:20:00|0|114|75535
2019-10-28 07:30:00|1|141|75550
2019-10-28 07:40:00|1|203|75575
2019-10-28 07:50:00|1|203|75575
2019-10-28 08:00:00|1|203|75575
...
2019-10-28 15:30:00|1|144|79397
2019-10-28 15:40:00|1|127|79414
2019-10-28 15:50:00|0|113|79427
2019-10-28 16:00:00|0|106|79437
2019-10-28 16:10:00|0| 99|79443
2019-10-28 16:20:00|0| 96|79445
2019-10-28 16:30:01|0| 96|79446
2019-10-28 16:40:00|0|  0|79446
2019-10-28 16:50:00|0|  0|79446
2019-10-28 17:00:00|0|  0|79446
2019-10-28 17:10:00|0|  0|79446
对于给定的一天,我希望提取如下值,删除idx未更改但仅在一天开始和结束时更改的数据:

2019-10-28 07:00:00|0|0|75522   -- Remove all unchanged values before
2019-10-28 07:10:00|0|103|75526
2019-10-28 07:20:00|0|114|75535
2019-10-28 07:30:00|1|141|75550
2019-10-28 07:40:00|1|203|75575 
2019-10-28 07:50:00|1|203|75575 -- Keep this
2019-10-28 08:00:00|1|203|75575 -- Keep this
...
2019-10-28 15:30:00|1|144|79397
2019-10-28 15:40:00|1|127|79414
2019-10-28 15:50:00|0|113|79427
2019-10-28 16:00:00|0|106|79437
2019-10-28 16:10:00|0|99|79443
2019-10-28 16:20:00|0|96|79445
2019-10-28 16:30:01|0|96|79446  -- Remove all unchanged values after
我尝试使用LAG来计算第行和第1行之间的idx增量

SELECT ts, i, p, idx, idx - LAG (idx, 1, idx) OVER (ORDER BY ts) 
FROM my_table 
WHERE DATE(ts) = '2019-10-28'
ORDER BY ts ASC
结果是有希望的:

2019-10-28 06:00:01|0|0|75522|0   -- To be removed 
2019-10-28 06:10:00|0|0|75522|0   -- To be removed 
2019-10-28 06:20:00|0|0|75522|0   -- To be removed 
2019-10-28 06:30:00|0|0|75522|0   -- To be removed 
2019-10-28 06:40:00|0|0|75522|0   -- To be removed 
2019-10-28 06:50:00|0|0|75522|0   -- To be removed 
2019-10-28 07:00:00|0|0|75522|0   -- Keep this
2019-10-28 07:10:00|0|103|75526|4
2019-10-28 07:20:00|0|114|75535|9
2019-10-28 07:30:00|1|141|75550|15
2019-10-28 07:40:00|1|203|75575|15 
2019-10-28 07:50:00|1|203|75575|0 -- Keep this
2019-10-28 08:00:00|1|203|75575|0 -- Keep this
...
2019-10-28 15:30:00|1|144|79397|20
2019-10-28 15:40:00|1|127|79414|17
2019-10-28 15:50:00|0|113|79427|13
2019-10-28 16:00:00|0|106|79437|10
2019-10-28 16:10:00|0|99|79443|6
2019-10-28 16:20:00|0|96|79445|2
2019-10-28 16:30:01|0|96|79446|1
2019-10-28 16:40:00|0|0|79446|0   -- Keep this
2019-10-28 16:50:00|0|0|79446|0   -- To be removed 
2019-10-28 17:00:00|0|0|79446|0   -- To be removed 
2019-10-28 17:10:00|0|0|79446|0   -- To be removed  
现在,我如何调整查询以删除所有前0和所有后0,但保留最后的前0和后0?请跟随我:-!不触摸中间值,甚至0个值?

在我的Python代码中,通过后期处理是否更好


编辑19年10月31日:idx列包含始终递增的数据,除非源读取发生更改。在我的例子中,我从消耗量表中获取数据。但是电力公司决定更换电表。。。现在从0重新启动。因此,最好从时间戳+当天第一条/最后一条记录的值变化来分析天数

我想你可以从你的初始数据中做到这一点:

SELECT * FROM
    (
    SELECT T1.*, 
        MIN(TS) OVER (PARTITION BY IDX) AS MIN_TS,
        MAX(TS) OVER (PARTITION BY IDX) AS MAX_TS,
        MIN(TS) OVER () AS MIN_GLOBAL_TS,
        MAX(TS) OVER () AS MAX_GLOBAL_TS
    FROM TABLENAME T1
    ) T2
WHERE ((TS = MIN_TS OR TS = MAX_TS) 
    and TS !=  MIN_GLOBAL_TS 
    and TS !=  MAX_GLOBAL_TS)
    or MIN_TS = MAX_TS
在这里,您可以找到任何给定idx的第一个和最后一个时间戳,然后选择那些字符串,哪个时间戳是第一个或最后一个,同时还要检查一天的开始和结束全局最小值和最大值,并删除对应于它们的值

我假设您的时间戳是日期格式的


如果在一天的结束或开始时没有重复的值,则返回该行的已编辑查询。

假设您使用MySQL,并且您的idx值随着您使用以下SQL的时间的增加而增加

SELECT ts, i, p, idx
FROM (
    SELECT *,
        min(ts) OVER (partition by date(ts), idx) min_ts_per_day_idx,
        max(ts) OVER (partition by date(ts), idx) max_ts_per_day_idx,
        min(idx) OVER (partition by date(ts)) min_idx_per_day,
        max(idx) OVER (partition by date(ts)) max_idx_per_day
    FROM tab
) t
WHERE (idx != min_idx_per_day and idx != max_idx_per_day) or
  (idx = min_idx_per_day and ts = max_ts_per_day_idx) or
  (idx = max_idx_per_day and ts = min_ts_per_day_idx)

如果您每天只有一个idx,则不清楚应该返回什么。在这种情况下,我的解决方案返回第一行和最后一行。

我将其解释为:

保留idx值不为零或下一个或上一个值不为零的所有记录。 过滤掉第一个非零之前或之前的所有其他记录 过滤掉最后一个非零后的所有其他记录 这里有一种方法:

select t.*
from (select t.*,
             lag(idx) over (partition by date(ts) order by date ts) as prev_idx,
             lead(idx) over (partition by date(ts) order by date ts) as next_idx,
             min(case when idx <> 0 then ts end) over (partition by date(ts)) as first_ts_not0,
             max(case when idx <> 0 then ts end) over (partition by date(ts)) as last_ts_not0,
      from my_table t
     ) t
where (idx <> 0 or prev_idx <> 0 or next_idx <> 0) or
      (ts >= first_ts_not0 and
       ts <= last_ts_not0
      );

我们可以假设idx值单调增加吗?您使用mysql还是sqlite?您好@RadimBača,谢谢您的提问。idx是一个10分钟的时间戳,所以它总是会增加。我使用sqlite3 db。我喜欢你的解决方案,但是,如果你在数据中有更多的天数@Radim I agree,但问题状态为给定的一天。@Radim另外,你是否检查了你在多天样本中的答案?我检查了它,我怀疑时间戳为“2019-10-29 8:00:00”的字符串不应该在那里。我在答复中对此作了评论。我相信8:00:00应该在那里,因为这是一天中最后一个idx的第一排。我试图回答你们两个,首先说谢谢你们花时间解决这个棘手的问题,至少对我来说是这样!。在我的数据库中,我收集了我多年的数据,但我的查询应该返回给定日期的数据,作为执行查询的函数的参数传递@RadimBača 8:00:00 AM并不是我为简化示例而使用“…”的那天的最后一个idx。在这个特定的示例中,我需要将所有数据保持在[7:00:00到16:40:00]之间。我在我的数据集上测试了您的查询,但我会避免依赖idx。。。我没有提供请求,仪表已更改,idx已从0重新启动。我真的更愿意分析值,从一天开始直到遇到第一个非冗余数据,从一天结束都一样。你好@Gordon,我不明白你的方法。在my DB idx中,值不是0,除非当仪表更改时,它从0重新开始。我猜你是从我最初的提议中重新开始的。@Fabrice。你的问题是:但是保持最后一个0,最后一个0你跟我来:-!。根据您所描述的,0值似乎是一个特殊值。是的,您是对的,但此语句基于我的查询建议的结果。你对此有详细说明吗?如果是的话,我必须考虑你的查询中的‘t’是我上面查询的结果吗?@法布里斯…不,它只是你表的别名。如果你把我的原始表放在我的问题的开头,Idx列中没有0,除非计数从0开始,这是10年中发生的一次。