【代码备忘录】复杂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
相关推荐
撸猫79119 分钟前
HttpSession 的运行原理
前端·后端·cookie·httpsession
嘵奇1 小时前
Spring Boot中HTTP连接池的配置与优化实践
spring boot·后端·http
子燕若水1 小时前
Flask 调试的时候进入main函数两次
后端·python·flask
程序员爱钓鱼1 小时前
跳转语句:break、continue、goto -《Go语言实战指南》
开发语言·后端·golang·go1.19
Persistence___2 小时前
SpringBoot中的拦截器
java·spring boot·后端
嘵奇2 小时前
Spring Boot 跨域问题全解:原理、解决方案与最佳实践
java·spring boot·后端
景天科技苑4 小时前
【Rust泛型】Rust泛型使用详解与应用场景
开发语言·后端·rust·泛型·rust泛型
lgily-12256 小时前
常用的设计模式详解
java·后端·python·设计模式
意倾城7 小时前
Spring Boot 配置文件敏感信息加密:Jasypt 实战
java·spring boot·后端
火皇4057 小时前
Spring Boot 使用 OSHI 实现系统运行状态监控接口
java·spring boot·后端