SQL思路解析:窗口滑动的应用
目录
🎯 问题目标
第一步:从数据中我们能直接得到什么?
第二步:我们想要的“7天窗口”长什么样?
第三步:SQL 怎么表达“某一天的前六天”?
🔍JOIN 比窗口函数更灵活
第四步:每个窗口要计算什么?
第五步:怎么避免不满 7 天的窗口?
最终完整 SQL
表: Customer+---------------+---------+
| Column Name | Type |
+---------------+---------+
| customer_id | int |
| name | varchar |
| visited_on | date |
| amount | int |
+---------------+---------+
在 SQL 中,(customer_id, visited_on) 是该表的主键。
该表包含一家餐馆的顾客交易数据。
visited_on 表示 (customer_id) 的顾客在 visited_on 那天访问了餐馆。
amount 是一个顾客某一天的消费总额。
你是餐馆的老板,现在你想分析一下可能的营业额变化增长(每天至少有一位顾客)。
计算以 7 天(某日期 + 该日期前的 6 天)为一个时间段的顾客消费平均值。average_amount
要 保留两位小数。
结果按 visited_on
升序排序。
返回结果格式的例子如下。
示例 1:输入:
Customer 表:
+-------------+--------------+--------------+-------------+
| customer_id | name | visited_on | amount |
+-------------+--------------+--------------+-------------+
| 1 | Jhon | 2019-01-01 | 100 |
| 2 | Daniel | 2019-01-02 | 110 |
| 3 | Jade | 2019-01-03 | 120 |
| 4 | Khaled | 2019-01-04 | 130 |
| 5 | Winston | 2019-01-05 | 110 |
| 6 | Elvis | 2019-01-06 | 140 |
| 7 | Anna | 2019-01-07 | 150 |
| 8 | Maria | 2019-01-08 | 80 |
| 9 | Jaze | 2019-01-09 | 110 |
| 1 | Jhon | 2019-01-10 | 130 |
| 3 | Jade | 2019-01-10 | 150 |
+-------------+--------------+--------------+-------------+
输出:
+--------------+--------------+----------------+
| visited_on | amount | average_amount |
+--------------+--------------+----------------+
| 2019-01-07 | 860 | 122.86 |
| 2019-01-08 | 840 | 120 |
| 2019-01-09 | 840 | 120 |
| 2019-01-10 | 1000 | 142.86 |
+--------------+--------------+----------------+
解释:
第一个七天消费平均值从 2019-01-01 到 2019-01-07 是restaurant-growth/restaurant-growth/ (100 + 110 + 120 + 130 + 110 + 140 + 150)/7 = 122.86
第二个七天消费平均值从 2019-01-02 到 2019-01-08 是 (110 + 120 + 130 + 110 + 140 + 150 + 80)/7 = 120
第三个七天消费平均值从 2019-01-03 到 2019-01-09 是 (120 + 130 + 110 + 140 + 150 + 80 + 110)/7 = 120
第四个七天消费平均值从 2019-01-04 到 2019-01-10 是 (130 + 110 + 140 + 150 + 80 + 110 + 130 + 150)/7 = 142.86
来源:Leecode
🎯 问题目标
先问自己最本质的问题:
我想得到的到底是什么?
你想得到:
-
某一天(比如
2019-01-07
)为 窗口最后一天 -
以它为终点往前推 6 天(共 7 天)的所有消费数据
-
求这 7 天的总消费额和平均消费额(平均保留两位小数)
-
然后按日期升序列出每个窗口的情况
第一步:从数据中我们能直接得到什么?
我们原始数据是:
| customer_id | name | visited_on | amount |
|-------------|-------|------------|--------|
| 1 | Jhon | 2019-01-01 | 100 |
| 2 | Daniel| 2019-01-02 | 110 |
| ... | ... | ... | ... |
这是“按顾客”记录的交易数据。
原始数据是“每个顾客某天消费了多少”,而我们不关心顾客是谁,只关心 每一天总共有多少消费。
为了达成这个目标,你最小的可操作单位是:
✅ 每一天的“总营业额”
所以,第一步我们应该做的是:
SELECTvisited_on,SUM(amount) AS total_amount
FROM Customer
GROUP BY visited_on
得到了:
| visited_on | total_amount |
|------------|--------------|
| 2019-01-01 | 100 |
| 2019-01-02 | 110 |
| 2019-01-03 | 120 |
| ... | ... |
第二步:我们想要的“7天窗口”长什么样?
比如你想分析 2019-01-07
这个窗口,它包括:
-
2019-01-01
-
2019-01-02
-
2019-01-03
-
2019-01-04
-
2019-01-05
-
2019-01-06
-
2019-01-07
我们要把这 7 天的金额加总后求平均。
换句话说,对于每一个日期 D
,你要去找所有日期 D'
,满足:
D' >= D - 6 天 AND D' <= D
,然后求 sum(amount)
第三步:SQL 怎么表达“某一天的前六天”?
想象一下,窗口要对比谁和谁?
我们要让每一行(例如日期是 2019-01-10
)“看见”自己之前 6 天的数据。但 SQL 是面向集合的语言,每一行默认不能看见其他行。
怎么让一行“看到”它前面的几天?答案是:自连接(JOIN)!
SELECTc1.visited_on, -- 作为窗口的当前“右端点”c2.visited_on, -- 被扫描比较的行
FROM (SELECT visited_on, SUM(amount) AS daily_totalFROM CustomerGROUP BY visited_on
) c1
JOIN (SELECT visited_on, SUM(amount) AS daily_totalFROM CustomerGROUP BY visited_on
) c2ON c2.visited_on BETWEEN DATE_SUB(c1.visited_on, INTERVAL 6 DAY) AND c1.visited_on
这个 JOIN 的意思是:
对于每一行 c1,找出所有 c2,使得 c2.visited_on
落在 c2 之前 6 天之内。
也就是说,每一行 c1 会配对出一个 7 天的“时间窗口”数据集 c2。
就像下面这个例子:
c1.visited_on | c2.visited_on(符合条件) |
---|---|
2019-01-07 | 2019-01-01 ~ 2019-01-07 |
2019-01-08 | 2019-01-02 ~ 2019-01-08 |
2019-01-09 | 2019-01-03 ~ 2019-01-09 |
2019-01-10 | 2019-01-04 ~ 2019-01-10 |
你可以理解为:“c1 的每一天”,都配对了“过去七天的 c2”,这就模拟出“滑动窗口”的行为了!
🔍JOIN 比窗口函数更灵活
在“时间窗口”这种分析中,数据可能并不是每天都有,或者每天不止一条记录,比如:
| visited_on | amount |
|--------------|--------|
| 2024-01-01 | 100 |
| 2024-01-01 | 80 |
| 2024-01-03 | 200 |
这种不连续、一天多条的情况,用 OVER (ORDER BY visited_on ROWS ...)
是不靠谱的,因为行数 ≠ 时间!
而 JOIN
这种方式,直接按时间范围配对,不依赖数据是否连续,每天有多少条都不影响。
第四步:每个窗口要计算什么?
你想要的就是:
-
c1.visited_on
:当前窗口的最后一天 -
SUM(c2.amount)
:这 7 天的总金额 -
ROUND(SUM(c2.amount) / 7, 2)
:这 7 天的平均值(保留两位小数)
第五步:怎么避免不满 7 天的窗口?
比如当你分析 2019-01-02
时,它前面只有两天的数据(01、02),这是 不满 7 天的窗口,要排除掉。
这时候就要加一条语句:
HAVING COUNT(DISTINCT c2.visited_on) = 7
意思是:只有当这 7 天真的有 7 个不同的日期数据,才纳入最终结果。
最终完整 SQL
把上述分析组合起来,完整 SQL 如下:
SELECT c1.visited_on,SUM(c2.daily_total) AS amount,ROUND(SUM(c2.daily_total)/7, 2) AS average_amount
FROM (SELECT visited_on, SUM(amount) AS daily_totalFROM CustomerGROUP BY visited_on
) c1
JOIN (SELECT visited_on, SUM(amount) AS daily_totalFROM CustomerGROUP BY visited_on
) c2ON c2.visited_on BETWEEN DATE_SUB(c1.visited_on, INTERVAL 6 DAY) AND c1.visited_on
GROUP BY c1.visited_on
HAVING COUNT(c2.visited_on) = 7
ORDER BY c1.visited_on;
问题层级 | 解释 |
---|---|
本质问题 | 想知道某天 + 前六天的消费总和和平均 |
可直接获取的数据 | 每天的顾客消费记录(可汇总) |
怎么形成7天窗口 | 用自连接 + 日期范围:BETWEEN D - 6 AND D |
如何计算 | 汇总 amount,平均除以 7 并 ROUND |
如何过滤不满7天窗口 | HAVING COUNT(DISTINCT c2.visited_on) = 7 |
最终排序 | 按 visited_on 升序展示 |
相关文章:
SQL思路解析:窗口滑动的应用
目录 🎯 问题目标 第一步:从数据中我们能直接得到什么? 第二步:我们想要的“7天窗口”长什么样? 第三步:SQL 怎么表达“某一天的前六天”? 🔍JOIN 比窗口函数更灵活 第四步&am…...

Rust 学习笔记:Box<T>
Rust 学习笔记:Box Rust 学习笔记:Box<T\>Box\<T> 简介使用 Box\<T\> 在堆上存储数据启用带有 box 的递归类型关于 cons 列表的介绍计算非递归类型的大小使用 Box\<T\> 获取大小已知的递归类型 Rust 学习笔记:Box<…...
C# 从 ConcurrentDictionary 中取出并移除第一个元素
C# 从 ConcurrentDictionary 中取出并移除第一个元素 要从 ConcurrentDictionary<byte, int> 中取出并移除第一个元素,需要结合 遍历 和 原子移除操作。由于 ConcurrentDictionary 是无序集合,"第一个元素" 通常是指最早添加的元素&…...

操作系统学习(十三)——Linux
一、Linux Linux 是一种类 Unix 的自由开源操作系统内核,由芬兰人 Linus Torvalds 于 1991 年首次发布。如今它广泛应用于服务器、桌面、嵌入式设备、移动设备(如 Android)等领域。 设计思想: 原则描述模块化与可移植性Linux 内…...

NLP学习路线图(二十二): 循环神经网络(RNN)
在自然语言处理(NLP)的广阔天地中,序列数据是绝对的核心——无论是流淌的文本、连续的语音还是跳跃的时间序列,都蕴含着前后紧密关联的信息。传统神经网络如同面对一幅打散的拼图,无法理解词语间的顺序关系,…...

每日一C(1)C语言的内存分布
目录 代码区 常量区 全局/静态区 初始化数据段(.data) 未初始化数据段(.bss) 堆区 栈区 总结 今天我们学习的是C语言的内存分布,以及这些分区所存储的内容和其特点。今天的思维导图如下。 C语言作为一款直接处…...

Photoshop使用钢笔绘制图形
1、绘制脸部路径 选择钢笔工具,再选择“路径”。 基于两个点绘制一个弯曲的曲线 使用Alt键移动单个点,该点决定了后续的曲线方向 继续绘制第3个点 最后一个点首尾是同一个点,使用钢笔保证是闭合回路。 以同样的方式绘制2个眼睛外框。 使用椭…...

应用层协议:HTTP
目录 HTTP:超文本传输协议 1.1 HTTP报文 1.1.1 请求报文 1.1.2 响应报文 1.2 HTTP请求过程和原理 1.2.1 请求过程 1、域名(DNS)解析 2、建立TCP连接(三次握手) 3、发送HTTP请求 4、服务器处理请求 5、返回H…...

复习——C++
1、scanf和scanf_s区别 2、取地址,输出 char ba; char* p&b; cout<<*p; cout<<p; p(char*)"abc"; cout<<*p; cout<<p; cout<<(void*)p; 取地址,把b的地址给p 输出*p,是输出p的空间内的值…...

SPI通信协议(软件SPI读取W25Q64)
SPI通信协议 文章目录 SPI通信协议1.SPI通信2.SPI硬件和软件规定2.1SPI硬件电路2.2移位示意图2.3SPI基本时序单元2.3.1起始和终止条件2.3.2交换一个字节(模式1) 2.4SPI波形分析(辅助理解)2.4.1发送指令2.4.2指定地址写2.4.3指定地…...
PostgreSQL-基于PgSQL17和11版本导出所有的超表建表语句
最新版本更新 https://code.jiangjiesheng.cn/article/368?fromcsdn 推荐 《高并发 & 微服务 & 性能调优实战案例100讲 源码下载》 1. 基于pgsql 17.4 研究 查询psql版本:SELECT version(); 查看已知1条建表语句和db中数据关系 SELECT create_hypert…...

JavaWeb:前后端分离开发-部门管理
今日内容 前后端分离开发 准备工作 页面布局 整体布局-头部布局 Container 布局容器 左侧布局 资料\04. 基础文件\layout/index.vue <script setup lang"ts"></script><template><div class"common-layout"><el-containe…...
ArcGIS计算多个栅格数据的平均栅格
3种方法计算多个栅格数据的平均栅格 1->使用“ 栅格计算器”工具 原理就是把多幅影像数据相加,然后除以个数,就能得到平均栅格。 2-> 使用“像元统计数据”工具,如果是ArcGIS pro,则是“像元统计”工具。使用这个工具可以…...

字节开源FlowGram:AI时代可视化工作流新利器
字节终于开源“扣子”同款引擎了!FlowGram:AI 时代的可视化工作流利器 字节FlowGram创新性地融合图神经网络与多模态交互技术,构建了支持动态拓扑重构的可视化流程引擎。该系统通过引入 f ( G ) ( V ′ , E ′ ) f(\mathcal{G})…...
如何选择合适的分库分表策略
选择合适的分库分表策略需要综合考虑业务特点、数据规模、访问模式、技术成本等多方面因素。以下是系统性的选择思路和关键决策点: 一、核心决策因素 业务需求分析 数据规模:当前数据量(如亿级)、增长速度(如每日新增百…...

(LeetCode 每日一题)3403. 从盒子中找出字典序最大的字符串 I (贪心+枚举)
题目:3403. 从盒子中找出字典序最大的字符串 I 题目:贪心枚举字符串,时间复杂度0(n)。 最优解的长度一定是在[1,n-numFriends]之间。 字符串在前缀都相同的情况下,长度越长越大。 C版本: class Solution { public:st…...

GPIO的内部结构与功能解析
一、GPIO总体结构 总体构成 1.APB2(外设总线) APB2总线是微控制器内部连接CPU与外设(如GPIO)的总线,负责CPU对GPIO寄存器的读写访问,支持低速外设通信 2.寄存器 控制GPIO的配置(输入/输出模式、上拉/下拉等&#x…...
Python训练打卡Day42
Grad-CAM与Hook函数 知识点回顾 回调函数lambda函数hook函数的模块钩子和张量钩子Grad-CAM的示例 在深度学习中,我们经常需要查看或修改模型中间层的输出或梯度。然而,标准的前向传播和反向传播过程通常是一个黑盒,我们很难直接访问中间层的信…...
深度学习中的负采样
深度学习中的负采样 负采样(Negative Sampling) 是一种在训练大型分类或概率模型(尤其是在输出类别很多时)中,用来加速训练、降低计算量的方法。 它常用于: 词向量训练(如 Word2Vecÿ…...

php7+mysql5.6单用户中医处方管理系统V1.0
php7mysql5.6中医处方管理系统说明文档 一、系统简介 ----------- 本系统是一款专为中医诊所设计的处方管理系统,基于PHPMySQL开发,不依赖第三方框架,采用原生HTML5CSS3AJAX技术,适配手机和电脑访问。 系统支持药品管理、处方开…...
Java 大视界 — Java 大数据在智能安防视频监控中的异常事件快速响应与处理机制
/*Java 大数据在智能安防视频监控中的异常事件快速响应与处理机制(简化示例)*/// 1. Event.java - 异常事件模型 package com.security.model;public class Event {private String id;private String type; // 如: "入侵", "火警"pr…...

智慧物流园区整体解决方案
该智慧物流园区整体解决方案借助云计算、物联网、ICT 等技术,从咨询规划阶段介入,整合供应链上下游资源,实现物流自动化、信息化与智能化。方案涵盖智慧仓储管理(如自动化立体仓储系统、温湿度监控)、智慧物流(运输管理系统 TMS、GPS 监控)、智慧车辆管理(定位、调度、…...
审批流程管理系统开发记录:layui前端交互的实践
一、需求拆解与技术选型 本次开发围绕企业审批流程管理场景,需实现以下核心功能: 前端申请表单与流程进度可视化底部滑动审批弹窗交互多版本MySQL数据库支持流程数据的增删改查与状态管理技术栈选择: 前端采用LayUI框架,利用其时间线组件(lay-timeline)实现流程进度展示…...

【会员专享数据】1960—2023年我国省市县三级逐年降水量数据(Shp/Excel格式)
之前我们分享过1960-2023年我国0.1分辨率的逐日、逐月、逐年降水栅格数据(可查看之前的文章获悉详情),是研究者Jinlong Hu与Chiyuan Miao分享在Zenodo平台上的数据,很多小伙伴拿到数据后反馈栅格数据不太方便使用,问我…...
2025年精通MVCC
今年找工作,无一例外又问到了MVCC这个知识点。几乎每次换工作都会被问到这个面试有用,工作毫无 * 用的知识。但是环境就是这样,既然如此,我们用一篇文章彻底搞懂MVCC 1.MVCC是什么 MVCC(Multi-Version Concurrency C…...
硬路由与软路由
目录 核心区别 ⚙️ 性能与功能定位 如何选择? 核心区别 硬路由: 本质: 专用的硬件设备。构成: 厂家将特定的路由器操作系统(通常是高度定制化、封闭或精简的)固化在专用的硬件平台上。硬件:…...

OpenCV C++ 心形雨动画
❤️ OpenCV C 心形雨动画 ❤️ 本文将引导你使用 C 和 OpenCV 库创建一个可爱的心形雨动画。在这个动画中,心形会从屏幕顶部的随机位置落下,模拟下雨的效果。使用opencv定制自己的专属背景 目录 简介先决条件核心概念实现步骤 创建项目定义心形结构…...

Fullstack 面试复习笔记:Java 基础语法 / 核心特性体系化总结
Fullstack 面试复习笔记:Java 基础语法 / 核心特性体系化总结 上一篇笔记:Fullstack 面试复习笔记:操作系统 / 网络 / HTTP / 设计模式梳理 目前上来说,这个系列的笔记本质上来说,是对不理解的知识点进行的一个梳理&…...

安卓Compose实现鱼骨加载中效果
安卓Compose实现鱼骨加载中效果 文章目录 安卓Compose实现鱼骨加载中效果背景与简介适用场景Compose骨架屏与传统View实现对比Shimmer动画原理简介常见问题与优化建议参考资料 本文首发地址 https://h89.cn/archives/404.html 背景与简介 在移动应用开发中,加载中占…...
使用qt 定义全局钩子 捕获系统的键盘事件
使用qt 定义全局钩子 捕获系统的键盘事件 即使焦点不在自定义软件上,也能够触发 以下待接口代码: class Hook :public QObject { Q_OBJECT public: Hook(); enum Type { CTRL_E, CTRL_W, SPACE, Enter, C };//自定义枚举,定义“修改”、“撤回…...