【代码备忘录】复杂SQL写法案例(一)

复盘(马后炮)一下,给自己以后写复杂SQL时留点案例。

以下案例均为虚构,如有雷同皆为巧合~

以下解法仅考虑实现,未涉及性能~

信息

store_revenue_ranking门店营收排行表

id 主键,revenue 营收额,date_day 时间,store_name 门店名称

需求

列出门店营收排行榜连续三天及以上营收额大于或等于 100万门店。 返回结果按date_day升序排列展示。

现有数据示例

id revenue date_day store_name
1 120 20160101 江南皮革城
2 89 20160102 江南皮革城
3 181 20160103 江南皮革城
4 150 20160104 车迟国皮革城
5 99 20160105 梁山皮革城
6 145 20160106 车迟国皮革城
7 1231 20160107 车迟国皮革城
8 129 20160108 车迟国皮革城
9 300 20160109 车迟国皮革城

输出数据示例

id revenue date_day store_name
6 145 20160106 车迟国皮革城
7 1231 20160107 车迟国皮革城
8 129 20160108 车迟国皮革城
9 300 20160109 车迟国皮革城

解题思路

  • 首先,从排行表中筛选出营收额大于等于100万的记录。
  • 然后,对筛选结果按照门店和日期进行分组,并根据日期进行升序排序。在每个门店分组内,使用行号进行标记。
  • 接下来,再次对得到的结果进行嵌套查询,并根据门店和日期减去行号的组合进行分组。在每个组中找到最早的日期。继续在当前结果上筛选出拥有至少3个记录的组。
  • 将筛选的结果与排行表进行内连接,使用门店和日期进行关联。关联条件是门店相等并且日期大于等于最早的日期。最后返回与连接结果匹配排行表中的所有列,并按照日期进行升序排序。

完整SQL

vbnet 复制代码
SELECT s1.*
FROM store_revenue_ranking s1
INNER JOIN (
    SELECT store_name, MIN(date_day) AS start_day
    FROM (
        SELECT *, ROW_NUMBER() OVER (PARTITION BY store_name ORDER BY date_day) AS rn
        FROM store_revenue_ranking
        WHERE revenue >= 100
    ) AS t
    GROUP BY store_name, date_day - rn
    HAVING COUNT(*) >= 3
) AS s2 ON s1.store_name = s2.store_name AND s1.date_day >= s2.start_day
ORDER BY s1.date_day;

步骤拆解

片段一

sql 复制代码
SELECT *, ROW_NUMBER() OVER (PARTITION BY store_name ORDER BY date_day) AS rn
FROM store_revenue_ranking
WHERE revenue >= 100

SELECT *: 选择所有列。

ROW_NUMBER() OVER (PARTITION BY store_name ORDER BY date_day) AS rn: 创建名为rn的新列,用来为每个不同的store_name按照date_day进行排序并分配一个唯一的序号。

WHERE revenue >= 100: 添加一个条件,仅选择revenue值大于等于100的行。 这段SQL的目的是选取store_revenue_ranking表中revenue大于等于100的行,并为每个store_name按照date_day排序分配一个唯一的序号。

片段二

sql 复制代码
SELECT store_name, MIN(date_day) AS start_day
FROM (
片段一
) AS t
GROUP BY store_name, date_day - rn
HAVING COUNT(*) >= 3

SELECT store_name, MIN(date_day) AS start_day: 选择store_name列。date_day列的最小值,并将其命名为start_day的结果列。

GROUP BY store_name, date_day - rn: 按照store_name(date_day - rn)的组合进行分组,其中rn是前面查询结果中的序号列。

HAVING COUNT(*) >= 3: 对分组结果进行筛选,只保留满足条件的分组,其中具有相同store_name(date_day - rn)值的分组的行数大于等于3。

最后

vbnet 复制代码
SELECT s1.*
FROM store_revenue_ranking s1
INNER JOIN (
片段二
) AS s2 ON s1.store_name = s2.store_name AND s1.date_day >= s2.start_day
ORDER BY s1.date_day;

INNER JOIN s2 ON s1.store_name = s2.store_name AND s1.date_day >= s2.start_day: 进行内连接操作,将表s1和表s2根据store_name进行连接,并且要求s1date_day大于等于s2start_day。最后按照date_day进行升序排序。

涉及知识点

MIN()[1] 是一个聚合函数,用于返回一组值中的最小值。它可以应用于数字、日期、字符串等不同类型的数据。 使用MIN()函数的一般语法如下:

sql 复制代码
SELECT MIN(column_name) FROM table_name;

ROW_NUMBER() OVER()[1] 是在MySQL中用于为结果集中的每一行分配一个唯一的行号。ROW_NUMBER()通常与 OVER() 子句一起使用,其中可以指定排序方式和分组条件。 一般语法如下:

sql 复制代码
SELECT ROW_NUMBER() OVER (ORDER BY column_name) AS row_number, column_name FROM table_name;

HAVING [2]在MySQL中,HAVING子句用于在GROUP BY子句之后过滤结果。它允许你根据聚合函数的计算结果对结果集进行筛选。 通常,在使用GROUP BY子句对结果进行分组之后,你可以使用HAVING子句根据聚合函数的结果来筛选分组后的数据。与WHERE子句不同,HAVING子句可以使用聚合函数和列名来进行过滤。 以下是HAVING子句的基本语法:

sql 复制代码
SELECT 列1, 列2, ...
FROM 表名
GROUP BY 列1, 列2, ...
HAVING 条件;

参考文献

csharp 复制代码
[1] . Mysql函数 . 菜鸟教程 . https://www.runoob.com/mysql/mysql-functions.html
[2] . MySQL 处理重复数据 . 菜鸟教程 . https://www.runoob.com/mysql/mysql-handling-duplicates.html
相关推荐
盛派网络小助手1 小时前
微信 SDK 更新 Sample,NCF 文档和模板更新,更多更新日志,欢迎解锁
开发语言·人工智能·后端·架构·c#
∝请叫*我简单先生2 小时前
java如何使用poi-tl在word模板里渲染多张图片
java·后端·poi-tl
zquwei3 小时前
SpringCloudGateway+Nacos注册与转发Netty+WebSocket
java·网络·分布式·后端·websocket·网络协议·spring
dessler3 小时前
Docker-run命令详细讲解
linux·运维·后端·docker
Q_19284999064 小时前
基于Spring Boot的九州美食城商户一体化系统
java·spring boot·后端
ZSYP-S4 小时前
Day 15:Spring 框架基础
java·开发语言·数据结构·后端·spring
Yuan_o_5 小时前
Linux 基本使用和程序部署
java·linux·运维·服务器·数据库·后端
程序员一诺5 小时前
【Python使用】嘿马python高级进阶全体系教程第10篇:静态Web服务器-返回固定页面数据,1. 开发自己的静态Web服务器【附代码文档】
后端·python
DT辰白6 小时前
如何解决基于 Redis 的网关鉴权导致的 RESTful API 拦截问题?
后端·微服务·架构