SQL xin手错误鉴赏以及成长小结
文章目录
- 1. 第一段错误代码
- 1.1 题目地址:SQL81 牛客的课程订单分析(五)
- 1.2 报错
- 1.3 解释
- 1.4 解决
- 1.5 反思
- 2. 第二段错误代码
- 2.1 题目地址:SQL82 牛客的课程订单分析(六)
- 2.2 报错
- 2.3 解释
- 2.4 解决
- 2.5 反思
- 第三段错误代码
- 题目地址:SQL79 牛客的课程订单分析(三)
- 报错
- 解释
- 解决
- 反思
- 十分重要:一路上的经验小结
1. 第一段错误代码
1.1 题目地址:SQL81 牛客的课程订单分析(五)
点击:SQL81 牛客的课程订单分析(五)
SELECT t.user_id, MIN(t.date), DATEADD(DAY, 1, MIN(t.date)), # MySQL不支持
# DATE_ADD(MIN(t.date), INTERVAL 1 DAY), # MySQL支持t.cnt
FROM (SELECT *,COUNT(*) OVER(PARTITION BY user_id) cntFROM order_infoWHERE date > '2025-10-15'AND status = 'completed'AND product_name IN ('C++', 'Java', 'Python')) t
WHERE t.cnt >= 2
GROUP BY t.user_id
ORDER BY t.user_id ASC
1.2 报错
Execution Error
SQL_ERROR_INFO: 'FUNCTION DATEADD does not exist'
1.3 解释
后来查询后得知,MySQL中对应DATEADD()
的用法是DATE_ADD()
,比如求日期date
后面的一天:
SQL
:DATEADD(DAY, 1, date)
MySQL
:DATE_ADD(date, DAY, 1)
1.4 解决
将相应部分改为 DATE_ADD(MIN(t.date), INTERVAL 1 DAY)
即可。
问题是该题其实还有一个bug
,虽然代码运行没有问题,但是不符合题目要求:
因为使用日期加一得到的结果并非一定是实际表格中该行数据的下一条数据!(这里实际上还是要使用排序窗口函数:ROW_NUMBER() OVER()
)
该题正确题解如下:
SELECT t.user_id, MIN(t.date) first_buy_date,
# DATEADD(DAY, 1, MIN(t.date)), # MySQL不支持
# DATE_ADD(MIN(t.date), INTERVAL 1 DAY), # MySQL支持MAX(t.date) second_buy_date,t.cnt
FROM (SELECT *,COUNT(*) OVER(PARTITION BY user_id) cnt,ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY date) rk_numFROM order_infoWHERE date > '2025-10-15'AND status = 'completed'AND product_name IN ('C++', 'Java', 'Python')) t
WHERE t.cnt >= 2 AND t.rk_num <= 2
GROUP BY t.user_id
ORDER BY t.user_id ASC
1.5 反思
这题过程中还遇到很多其他小问题,在这里记录一下:
1、使用排序 RANK()窗口函数时,使别名 rank
时发生了报错,应该选择用 rk
,或者给rank
加引号(SQL的别名最好避开使用一些关键字)
2、关于窗口函数 OVER()的用法小结
OVER()函数的四种情况:
(1)聚合函数(以cout为例)
① COUNT() OVER()
:总工资
② COUNT() OVER (PARTITION BY group)
:每个组的总工资
③ COUNT() OVER(PARTITION BY group ORDER BY date)
:每个组的累积总工资
④ COUNT() OVER(ORDER BY date)
:累积总工资
(2)排序
也是同理:PARTITION BY 用于分区,ORDER BY 用于累计,但是有一点区别:排序窗口函数OVER()中必须使用ORDER BY
,否则报错!</font.
2. 第二段错误代码
2.1 题目地址:SQL82 牛客的课程订单分析(六)
点击:SQL82 牛客的课程订单分析(六)
# 写出一个sql语句查询在2025-10-15以后,
# 同一个用户下单2个以及2个以上状态为购买成功的
# C++课程或Java课程或Python课程的订单id,是否拼团以及客户端名字信息,
# 最后一列如果是非拼团订单,则显示对应客户端名字,
# 如果是拼团订单,则显示NULL,并且按照order_info的id升序排序# order_info
# clientSELECT t.id, t.is_group_buy,IF(t.is_group_buy = 'Yes', NULL, t.name) AS client_name
FROM (SELECT *,COUNT(*) OVER(PARTITION BY user_id) AS cnt FROM order_info oLEFT JOIN client cON o.client_id = c.id WHERE date > '2025-10-15'AND status = 'completed'AND product_name IN ('C++', 'Java', 'Python')) AS t
WHERE t.cnt >= 2
ORDER BY t.id
2.2 报错
Execution ErrorSQL_ERROR_INFO: "Duplicate column name 'id'"
2.3 解释
报错原因是参与子查询中参与JOIN
的两个表存在相同名称的字段,且并非连接字段,即下图的id
2.4 解决
表格中有相同字段对的情况下,在select 字段的时候需要在其前面指定表格名。比如 SELECT xxx
改为SELECT a.xxx
正解如下:
# 写出一个sql语句查询在2025-10-15以后,
# 同一个用户下单2个以及2个以上状态为购买成功的
# C++课程或Java课程或Python课程的订单id,是否拼团以及客户端名字信息,
# 最后一列如果是非拼团订单,则显示对应客户端名字,
# 如果是拼团订单,则显示NULL,并且按照order_info的id升序排序# order_info
# clientSELECT t.id, t.is_group_buy,IF(t.is_group_buy = 'Yes', NULL, t.name) AS client_name
FROM (SELECT o.*, c.name, # 这里并非全选使用 *,而是指定表格o,为了排除表格c中重复的字段id(也可以选择在外表使用JOIN)COUNT(*) OVER(PARTITION BY user_id) AS cnt FROM order_info oLEFT JOIN client c # 这里不能使用 INNER JOINON o.client_id = c.id WHERE date > '2025-10-15'AND status = 'completed'AND product_name IN ('C++', 'Java', 'Python')) AS t
WHERE t.cnt >= 2
ORDER BY t.id
2.5 反思
其实这里问题的根源是我将JOIN
放在了FROM
后面的子查询中,而子查询的SELECT
中使用了全选*
,这是一条新手经验:使用了JOIN
,就要注意对重复字段进行指定表格,包括*
;
或者说,为了避免这种错误,最好将表格连接操作放在外表当中(而不是在子查询中使用JOIN操作)
第三段错误代码
题目地址:SQL79 牛客的课程订单分析(三)
点击:SQL79 牛客的课程订单分析(三)
# # 写出一个sql语句查询在2025-10-15以后,
# # 同一个用户下单2个以及2个以上
# # 状态为购买成功的C++课程或Java课程或Python课程的订单信息,
# # 并且按照order_info的id升序排序SELECT *
FROM order_info
WHERE date > '2025-10-15'AND (SELECT COUNT(*) OVER(PARTITION BY user_id ORDER BY product_name) FROM order_info ) >= 2 AND status = 'completed'AND product_name IN ('C++', 'Java', 'Python')
ORDER BY user_id ASC
报错
SQL_ERROR_INFO: 'Subquery returns more than 1 row'
解释
把窗口函数理解成为了一个数值,虽然窗口函数是在子查询中使用,但是其返回值本质上是”一列“数据,所以这里报错子查询返回超过了一行。
解决
将窗口函数所在的子查询放在FROM 语句
之后,再使用外层 SELECT 去取值
(其实上述代码还有一个BUG,即窗口函数中OVER()函数
的用法错误:若统计总数而不是累计总数,则不能在OVER()函数
中加ORDER BY
,这里不多做赘述)
使用窗口函数的正解如下:
# 方法二:窗口函数
SELECT t.id, t.user_id, t.product_name, t.status, t.client_id, t.date
FROM (SELECT *, COUNT(*) OVER(PARTITION BY user_id ORDER BY product_name) AS rankFROM order_infoWHERE date > '2025-10-15'AND status = 'completed'AND product_name IN ('C++', 'Java', 'Python')) AS t
WHERE t.rank >= 2
ORDER BY t.id ASC
反思
SQL的逻辑跟其他编程语言还是有些不一样,后续需要深入了解其”循环“的底层逻辑。
十分重要:一路上的经验小结
一、对SQL基本句型的本质理解
SELECT
的本质目的是列过滤,用于选出目标字段;
WHERE
的本质目的是行过滤,用于筛选出符合指定条件的所有行;
JOIN
的本质目的是进行列合并 + 行过滤,其本质是循环,用于多表连接;
GROUP BY
的本质目的是分组,也即分区+去重,且改变了表格本身;
HAVING
的本质目的是在分区内进行行过滤
窗口函数
的本质是类似开挂或者站在上帝视角,在不影响表格本身的情况下获取“影藏”信息,比如分区排序相关的信息
二、对SQL执行顺序的理解
三、其它汇总(一些常见问题)
SELECT相关
- SELECT 实际执行在 FROM、JOIN和 GROUP BY 之后,因此这些在SELECT前面的操作理论上都无法使用SELECT中的别名
GROUP BY
- 使用GROUP BY 之后,SELECT语句后面的字段要么是分组字段,要么是聚合字段,不能出现其它多余字段
HAVING
- HAVING 后可以直接跟聚合函数,这是SQL在WHERE之外增加HAVING语句的原因(参考链接:点击)
JOIN
- 使用 JOIN 的第一步就是判断是该使用INNER JOIN 还是 LEFT JOIN(最常用这两种)。
- JOIN 本质核心是循环(loop)。
LEFT JOIN
:返回包括左表中的所有记录和右表中联结字段相等的记录。
是针对左表的每一行,循环匹配右表,如果on条件匹配到了,就添加到结果集中,相当于两重for循环
,既完成了列合并,又完成了行过滤。注意,On后面的条件对Left的表没有作用,只对Right的表有过滤作用INNER JOIN
:只返回两个表中联结字段相等的行。其本质应该可以理解为跟LEFT JOIN 一样,只不过LEFT 表这次也会进行筛选。
- JOIN 之后的表格t 可能存在重复字段的情况,这种情况在使用字段的时候必须先指定表格名,比如
SELECT t.*
、SELECT t.id
- 为了避免(使用
SELECT *
)造成字段可能重复的情况,可以考虑优先将JOIN放在外表当中(而不是子查询中),或者时刻牢记这条标准:使用了JOIN就指定所有用到的字段
条件函数
- 只能用在 SELECT 之后,不可单独使用;如需在WHERE等语句使用条件函数的值,只能先将其放在一个子查询中,再进行SELECT取值
窗口函数
- 只能用在 SELECT 之后,不可单独使用;如需在WHERE等语句使用条件函数的值,只能先将其放在一个子查询中,再进行SELECT取值(注意:不是所有情况的窗口函数都要放在子查询,而是需要将从窗口函数放在
WHERE
中使用的时候)- 无论那种变种,窗口函数返回的是”一列“数据,而不像聚合函数有可能是一个数值,因此这个子查询一般是位于 FROM 后面,将窗口函数用于子查询SELECT之后,就给表格新增了一列数据,想要使用则使用上层SELECT去选取(理解这一点至关重要)
- 排序窗口函数必须在OVER()中使用
ORDER BY
子查询
- 关联子查询的用法
- 子查询使用位置的灵活使用:① FROM后面(增加新的字段到表格中) ② WHERE中(单独限定某个或某些字段)——参考链接:点击
特别注意:
- 聚合函数、窗口函数等不能单独使用,必须放在 SELECT语句中,所以想要在WHERE等语句使用这两类函数的值进行行过滤,则必须将其放在一个子查询中先获取这些聚合值或者窗口函数的值
- 窗口函数的返回值是一列数据
- HAVING 后可以直接跟聚合函数
参考:
- Left Join理解
SQL xin手错误鉴赏以及成长小结相关推荐
- SQL SERVER错误:已超过了锁请求超时时段。 (Microsoft SQL Server,错误: 1222)
在SSMS(Microsoft SQL Server Management Studio)里面,查看数据库对应的表的时候,会遇到"Lock Request time out period e ...
- sql server xp_readerrorlog SQL语句查看错误日志
sql server xp_readerrorlog SQL语句查看错误日志 xp_readerrorlog 一共有7个参数: 存档编号 日志类型(1为SQL Server日志,2为SQL Agent ...
- (provider: 共享内存提供程序, error: 0 - 管道的另一端上无任何进程。) (Microsoft SQL Server,错误: 233)...
------------------------------ 无法连接到 IFCA-LIUWEI/SQL2005. ------------------------------ 其他信息: 已成功与服 ...
- C#-修改图书借阅管理系统-错误与SQL server 2008错误、复制数据库
VS2012错误: *)不存在从对象类型 System.Object[] 到已知的托管提供程序本机类型的映射 public DataTable loadData2UserSearch(params o ...
- SQL数据库挂起 SQL数据库附加报错 SQL数据库824错误修复
SQL数据库挂起 SQL数据库附加报错 SQL数据库824错误修复 数据类型 MSSQL 2012 数据大小 4.5 GB 故障检测 附加数据库提示824错误 一般是由于断电非法关机导致页面损坏. 客 ...
- PL/SQL Developer的错误提示弹框的文本显示乱码问题
问题: Windows中文环境下,PL/SQL Developer的错误提示弹框文本为乱码,如下: 解决过程: 1.使用SELECT * FROM v$nls_parameters;查询得知服务器的字 ...
- SQL执行计划错误导致临时表空间不足
故障现象:临时表空间不足的问题已经报错过3次,客户也烦了,前两次都是同事添加5G的数据文件,目前已经达到40G,占用临时表空间主要是distinct 和group by 以及Union all 表数据 ...
- sql 除以_避免SQL除以零错误的方法
sql 除以 This article explores the SQL divide by zero error and various methods for eliminating this. ...
- mysql 错误提示_Mysql必读mysql常见的错误提示问题处理小结
<Mysql必读mysql常见的错误提示问题处理小结>要点: 本文介绍了Mysql必读mysql常见的错误提示问题处理小结,希望对您有用.如果有疑问,可以联系我们. 1.mysql服务无法 ...
最新文章
- 细说Android事件传递
- Spring Boot 中关于 %2e 的 Trick
- mysqldump 常用备份选项,只备份数据或结构的方法
- 关于Ad-hoc测试的基本知识
- FreeSql (二十五)延时加载
- linux定时任务的用法详解
- hibernate、easyui、struts2整合
- 零基础转行自学前端,怎么学习更系统?
- 《android 解析WebService》
- python plt 色卡
- Scrapy实战,利用Scrapy简单爬取新闻并将内容储存
- Docker容器监控cAdvisor
- BootStrap之导航navigation
- 我的大三,青山隐隐,绿水悠悠
- 「萌新上手Mac」安装软件,步骤超简单
- linux运维是什么
- c语言酒店信息系统的ppt,(完整版)C语言酒店管理系统
- 计算机系统要素:硬件描述语言HDL简介
- 担当力2:担责七步骤和担当力
- yolov5 test.py val.py detec.py 区别在哪里呢?
热门文章
- nmon结果分析工具_使用nmon analyzer 分析指标
- 大数据——何谓“大”
- linux 行首加特定字符_Linux:用sed命令在文本的行尾或行首添加字符
- php json输出对象的属性值,JavaScript_jquery动态遍历Json对象的属性和值的方法,1、遍历 json 对象的属性/ - phpStudy...
- 分组密码以及分组密码的模式
- 内网时间同步,ntp与ntpdate区别,与ntp服务器搭建
- C++ 什么是继承和派生
- 机器学习图像特征提取—颜色(RGB、HSV、Lab)特征提取并绘制直方图
- matlab提示参数不足,为什么提示输入参数条目不足?
- Uva207 PGA Tour Prize Money