某水表量每15分钟一报,然后某天示数清0了,重新报示值了 ,如何写sql 计算每日水量

要计算每日电量,需处理电表清零的情况。以下是针对不同数据库的解决方案:

方法思路

  1. 识别清零点:通过比较当前值与前一个值,若当前值明显变小(如小于前值的10%),则视为清零。
  2. 分段累计:将数据按清零点分段,每段单独累计电量。
  3. 每日汇总:按日期分组,累加每日各段的电量总和。

SQL 实现(以 MySQL 为例)

sql 复制代码
WITH RECURSIVE
-- 1. 添加行号和前一个值
t1 AS (
    SELECT
        `timestamp`,
        value,
        ROW_NUMBER() OVER (ORDER BY `timestamp`) AS rn,
        LAG(value) OVER (ORDER BY `timestamp`) AS prev_value
    FROM meter_data
),
-- 2. 标记清零点(当前值 < 前值的10%)
t2 AS (
    SELECT
        `timestamp`,
        value,
        rn,
        prev_value,
        CASE WHEN prev_value IS NOT NULL AND value < prev_value * 0.1 THEN 1 ELSE 0 END AS is_reset
    FROM t1
),
-- 3. 为每个清零点分配组ID(使用递归CTE)
t3 AS (
    SELECT
        `timestamp`,
        value,
        rn,
        is_reset,
        rn AS group_id
    FROM t2
    WHERE rn = 1
    
    UNION ALL
    
    SELECT
        t2.`timestamp`,
        t2.value,
        t2.rn,
        t2.is_reset,
        CASE WHEN t2.is_reset = 1 THEN t2.rn ELSE t3.group_id END
    FROM t2
    JOIN t3 ON t2.rn = t3.rn + 1
),
-- 4. 计算每组的增量并累加
t4 AS (
    SELECT
        `timestamp`,
        DATE(`timestamp`) AS date,
        value,
        group_id,
        value - LAG(value, 1, value) OVER (PARTITION BY group_id ORDER BY `timestamp`) AS increment
    FROM t3
)
-- 5. 按日期汇总电量(忽略负值,可能是异常或清零残留)
SELECT
    date,
    SUM(CASE WHEN increment >= 0 THEN increment ELSE 0 END) AS daily_energy
FROM t4
GROUP BY date
ORDER BY date;

其他数据库适配

  • PostgreSQL :与 MySQL 类似,但递归 CTE 语法更简洁:

    sql 复制代码
    WITH RECURSIVE t3 AS (...)
  • SQL Server :使用 ISNULL 替代 COALESCE,窗口函数语法相同。

  • Oracle :使用 CONNECT BY 替代递归 CTE,或用 MATCH_RECOGNIZE 简化模式匹配。

关键点说明

  1. 清零判断value < prev_value * 0.1 可根据实际情况调整阈值。
  2. 分组逻辑 :通过递归 CTE 为每个清零点分配新的 group_id
  3. 处理异常值SUM(CASE WHEN increment >= 0 ...) 过滤可能的负值(如清零残留)。

示例数据验证

假设有以下数据(每15分钟记录一次):

timestamp value
2023-01-01 00:00:00 100
2023-01-01 00:15:00 110
2023-01-01 23:45:00 900
2023-01-02 00:00:00 50
2023-01-02 23:45:00 800

计算结果:

date daily_energy
2023-01-01 800
2023-01-02 750

优化建议

  1. 预处理数据:若频繁查询,可将分组结果存储为物化视图。
  2. 索引优化 :确保 timestamp 字段有索引,加速排序和窗口函数计算。
  3. 动态阈值:根据历史数据动态调整清零判断阈值,提高准确性。
复制代码

方法二

sql 复制代码
 with a1 as(
	select  * 
	from (
   --查询有清0 的表
	   SELECT
		  Id, ProjectId, fd,val,dot,PointId,
			LAG(val) OVER (PARTITION BY  ProjectId,PointId ORDER BY dot asc,val desc) AS prev_value , 
			LAG(Id) OVER (PARTITION BY  ProjectId,PointId ORDER BY dot asc,val desc) AS prev_Id 
		
		FROM table1
		where fd >='2025-05-06'
	  and fd<='2025-05-07'
	   and dot<='2025-05-07 00:00:00'
	   and ProjectId=170132 
	) as  t
	where 1=1
	and val < prev_value * 0.1
 ),
 av3 as(
      --查询有清0 的表的最后一条数据
       select  *  from (
      select a.id,a.PointId,a.fd,  ROW_NUMBER() OVER (PARTITION BY  a.ProjectId,a.PointId ORDER BY a.dot desc,a.val desc) AS rn
	  from a1 t1,table1 a
	  where t1.PointId=a.pointId  and t1.dot<a.dot
	and  a.fd >='2025-05-06'
 and a.fd<='2025-05-07'
 and a.dot<='2025-05-07 00:00:00'
	) as t
	where t.rn=1
	  
 ) ,
 a2 as (
       --查询有清0 的表的前一条数据,当前数据 和最后一条数据的
       select   a.*,-1 as preItem  from a1,table1 a
	   where a1.prev_Id=a.Id 
	   union all
	   select   a.*,0 as preItem  from a1,table1 a
	   where a1.id=a.Id  
	   union all
	   select   a.*,1 as preItem  from av3 a3,table1 a
	   where a3.id=a.Id  
 )
 
 select  *  from  av3
相关推荐
辉灰笔记15 分钟前
MySQL8 意外关机/误移data目录导致服务启动失败 PID报错/权限报错 修复文档
linux·数据库·mysql·centos
麦聪聊数据1 小时前
电商大促数据库管控(下):数据需求小时级交付与全链路安全管控
数据库
喜欢的名字被抢了1 小时前
MySQL核心机制:事务、锁与存储引擎
数据库·sql·mysql·教程
JosieBook2 小时前
【数据库】时序数据库通过国测认证,TimechoAI开放体验:工业数据的“存”与“智”如何协同?
数据库·时序数据库
卓怡学长2 小时前
w263基于springboot + vue 健康饮食管理系统
java·数据库·vue.js·spring boot·spring·intellij-idea
阿演4 小时前
DataDjinn v0.2.11:SQL 编辑、AI 协作和表格操作继续打磨
数据库·人工智能·sql
智脑API平台5 小时前
Codex 国内使用会封号吗?账号安全、中转站和合规边界说明
数据库·redis·缓存
l1t5 小时前
duckdb 1.6dev新增的.manual命令
开发语言·数据库
辉灰笔记6 小时前
第一篇:MySQL8.0生产备份实战|XtraBackup全自动全量+增量备份(钉钉告警+异地Binlog归档)
数据库·mysql·钉钉·运维开发
山峰哥6 小时前
‌Explain实战:打开数据库执行计划的黑盒‌
大数据·服务器·数据库·sql·深度优先·宽度优先