某水表量每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
相关推荐
泊浮目11 分钟前
未来数据库硬件-网络篇
数据库·架构·云计算
静若繁花_jingjing25 分钟前
Redis线程模型
java·数据库·redis
飞翔的佩奇2 小时前
Java项目:基于SSM框架实现的忘忧小区物业管理系统【ssm+B/S架构+源码+数据库+毕业论文+开题报告】
java·数据库·mysql·vue·毕业设计·ssm框架·小区物业管理系统
亚马逊云开发者2 小时前
全景解读亚马逊云科技的 GenBI 解决方案:三大路径助力企业智能决策升级
sql·llm
ZWZhangYu8 小时前
LangChain 构建向量数据库和检索器
数据库·langchain·easyui
feifeigo1239 小时前
升级到MySQL 8.4,MySQL启动报错:io_setup() failed with EAGAIN
数据库·mysql·adb
火龙谷10 小时前
【nosql】有哪些非关系型数据库?
数据库·nosql
焱焱枫11 小时前
Oracle获取执行计划之10046 技术详解
数据库·oracle
双力臂40412 小时前
MyBatis动态SQL进阶:复杂查询与性能优化实战
java·sql·性能优化·mybatis
qq_3923971213 小时前
Redis常用操作
数据库·redis·wpf