某水表量每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
相关推荐
@小红花1 小时前
MySQL数据库从0到1
数据库·mysql·oracle
[听得时光枕水眠]1 小时前
MySQL基础(三)DQL(Data Query Language,数据查询语言)
数据库·mysql·oracle
我科绝伦(Huanhuan Zhou)1 小时前
深入解析Oracle SQL调优健康检查工具(SQLHC):从原理到实战优化
数据库·sql·oracle
神奇侠20245 小时前
Hive SQL常见操作
hive·hadoop·sql
一只叫煤球的猫5 小时前
MySQL 8.0 SQL优化黑科技,面试官都不一定知道!
后端·sql·mysql
寒山李白5 小时前
MySQL安装与配置详细讲解
数据库·mysql·配置安装
多多*6 小时前
微服务网关SpringCloudGateway+SaToken鉴权
linux·开发语言·redis·python·sql·log4j·bootstrap
文牧之6 小时前
PostgreSQL 的扩展pg_freespacemap
运维·数据库·postgresql
Leo.yuan7 小时前
数据库同步是什么意思?数据库架构有哪些?
大数据·数据库·oracle·数据分析·数据库架构