神奇的 SQL,Group By 真扎心,原来是这样!

本文探讨了SQL中GROUP BY后的SELECT限制,强调了为何不能直接引用原表非聚合列。讲解了ONLY_FULL_GROUP_BY模式的作用,以及如何在不同SQL模式下处理这种情况。重点在于理解聚合查询的层级逻辑和适用范围。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

GROUP BY 后 SELECT 列的限制

标准 SQL 规定,在对表进行聚合查询的时候,只能在 SELECT 子句中写下面 3 种内容:通过 GROUP BY 子句指定的聚合键、聚合函数(SUM 、AVG 等)、常量。我们来看个例子:

我们有 学生班级表(tbl_student_class) 以及 数据如下 :

DROP TABLE IF EXISTS tbl_student_class;
CREATE TABLE tbl_student_class (
  id int(8) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键',
  sno varchar(12) NOT NULL COMMENT '学号',
  cno varchar(5) NOT NULL COMMENT '班级号',
  cname varchar(20) NOT NULL COMMENT '班级名',
  PRIMARY KEY (id)
) COMMENT='学生班级表';

-- ----------------------------
-- Records of tbl_student_class
-- ----------------------------
INSERT INTO tbl_student_class VALUES ('1', '20190607001', '0607', '影视7班');
INSERT INTO tbl_student_class VALUES ('2', '20190607002', '0607', '影视7班');
INSERT INTO tbl_student_class VALUES ('3', '20190608003', '0608', '影视8班');
INSERT INTO tbl_student_class VALUES ('4', '20190608004', '0608', '影视8班');
INSERT INTO tbl_student_class VALUES ('5', '20190609005', '0609', '影视9班');
INSERT INTO tbl_student_class VALUES ('6', '20190609006', '0609', '影视9班');

我们想统计各个班(班级号、班级名)一个有多少人、以及最大的学号,我们该怎么写这个查询 SQL ?我想大家应该都会

SELECT cno,cname,count(sno),MAX(sno) 
FROM tbl_student_class
GROUP BY cno,cname;

可是有人会想了,cno 和 cname 本来就是一对一,cno 一旦确定,cname 也就确定了,那 SQL 是不是可以这么写 ?

SELECT cno,cname,count(sno),MAX(sno) 
FROM tbl_student_class
GROUP BY cno;

执行报错了:

[Err] 1055 - Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'test.tbl_student_class.cname' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

提示信息:SELECT 列表中的第二个表达式(cname)不在 GROUP BY 的子句中,同时它也不是聚合函数;这与 sql 模式:ONLY_FULL_GROUP_BY 不相容。

为什么 GROUP BY 之后不能直接引用原表(不在 GROUP BY 子句)中的列 ?莫急,我们慢慢往下看。

SQL 模式

MySQL 服务器可以在不同的 SQL 模式下运行,并且可以针对不同的客户端以不同的方式应用这些模式,具体取决于 sql_mode 系统变量的值。DBA 可以设置全局SQL模式以匹配站点服务器操作要求,并且每个应用程序可以将其会话 SQL 模式设置为其自己的要求。

模式会影响 MySQL 支持的 SQL 语法以及它执行的 数据验证检查,这使得在不同环境中使用MySQL以及将MySQL与其他数据库服务器一起使用变得更加容易。更多详情请查阅官网:Server SQL Modes。

MySQL 版本不同,内容会略有不同(包括默认值),查阅的时候注意与自身的 MySQL 版本保持一致。

SQL 模式主要分两类:语法支持类和数据检查类,常用的如下

语法支持类

  • ONLY_FULL_GROUP_BY

对于 GROUP BY 聚合操作,如果在 SELECT 中的列、HAVING 或者 ORDER BY 子句的列,没有在GROUP BY中出现,那么这个SQL是不合法的

  • ANSI_QUOTES

启用 ANSI_QUOTES 后,不能用双引号来引用字符串,因为它被解释为识别符,作用与 ` 一样。设置它以后,update t set f1="" …,会报 Unknown column ‘’ in field list 这样的语法错误

  • PIPES_AS_CONCAT

将 || 视为字符串的连接操作符而非 或 运算符,这和Oracle数据库是一样的,也和字符串的拼接函数 CONCAT() 相类似

  • NO_TABLE_OPTIONS

使用 SHOW CREATE TABLE 时不会输出MySQL特有的语法部分,如 ENGINE ,这个在使用 mysqldump 跨DB种类迁移的时候需要考虑

  • NO_AUTO_CREATE_USER

字面意思不自动创建用户。在给MySQL用户授权时,我们习惯使用 GRANT … ON … TO dbuser 顺道一起创建用户。设置该选项后就与oracle操作类似,授权之前必须先建立用户

数据检查类

  • NO_ZERO_DATE

认为日期 ‘0000-00-00’ 非法,与是否设置后面的严格模式有关

1、如果设置了严格模式,则 NO_ZERO_DATE 自然满足。但如果是 INSERT IGNORE 或 UPDATE IGNORE,’0000-00-00’依然允许且只显示warning;
2、如果在非严格模式下,设置了NO_ZERO_DATE,效果与上面一样,’0000-00-00’ 允许但显示warning;如果没有设置NO_ZERO_DATE,no warning,当做完全合法的值;
3、NO_ZERO_IN_DATE情况与上面类似,不同的是控制日期和天,是否可为 0 ,即 2010-01-00 是否合法;

  • NO_ENGINE_SUBSTITUTION

使用 ALTER TABLE 或 CREATE TABLE 指定 ENGINE 时, 需要的存储引擎被禁用或未编译,该如何处理。启用 NO_ENGINE_SUBSTITUTION 时,那么直接抛出错误;不设置此值时,CREATE用默认的存储引擎替代,ATLER不进行更改,并抛出一个 warning

  • STRICT_TRANS_TABLES

设置它,表示启用严格模式。注意 STRICT_TRANS_TABLES 不是几种策略的组合,单独指 INSERT、UPDATE 出现少值或无效值该如何处理:

1、前面提到的把 ‘’ 传给int,严格模式下非法,若启用非严格模式则变成 0,产生一个warning;
2、Out Of Range,变成插入最大边界值;
3、当要插入的新行中,不包含其定义中没有显式DEFAULT子句的非NULL列的值时,该列缺少值;

默认模式

当我们没有修改配置文件的情况下,MySQL 是有自己的默认模式的;版本不同,默认模式也不同

-- 查看 MySQL 版本
SELECT VERSION();

-- 查看 sql_mode
SELECT @@sql_mode;

在这里插入图片描述

我们可以看到,5.7.21 的默认模式包含:

ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

而第一个:ONLY_FULL_GROUP_BY 就会约束:当我们进行聚合查询的时候,SELECT 的列不能直接包含非 GROUP BY 子句中的列。那如果我们去掉该模式(从“严格模式”到“宽松模式”)呢 ?
在这里插入图片描述

我们发现,上述报错的 SQL

-- 宽松模式下 可以执行
SELECT cno,cname,count(sno),MAX(sno) 
FROM tbl_student_class
GROUP BY cno;

能正常执行了,但是一般情况下不推荐这样配置,线上环境往往是“严格模式”,而不是“宽松模式”;虽然案例中,无论是“严格模式”,还是“宽松模式”,结果都是对的,那是因为 cno 与 cname 唯一对应的,如果 cno 与 cname 不是唯一对应,那么在“宽松模式下” cname 的值是随机的,这就会造成难以排查的问题,有兴趣的可以去试试。那为什么会有 ONLY_FULL_GROUP_BY 模式呢 ? 我们继续往下看:

阶(order)是用来区分集合或谓词的阶数的概念。谓词逻辑中,根据输入值的阶数对谓词进行分类。

= 或者 BETWEEEN 等输入值为一行的谓词叫作"一阶谓词",而像 EXISTS 这样输入值为行的集合的谓词叫作"二阶谓词"(HAVING 的输入值也是集合,但它不是谓词)。以此类推,三阶谓词=输入值为"集合的集合"的谓词,四阶谓词=输入值为"集合的集合的集合"的谓词,但是 SQL 里并不会出现三阶以上的情况,所以不用太在意。

简单点如下图
在这里插入图片描述

谈到了阶,就不得不谈下集合论;集合论是 SQL 语言的根基,因为它的这个特性,SQL 也被称为面向集合语言。只有从集合的角度来思考,才能明白 SQL 的强大威力。通过上图,相信大家也都能看到,这里不做更深入的讲解了,有兴趣的可以去查相关资料。

为什么聚合后不能再引用原表中的列

很多人都知道聚合查询的限制,但是很少有人能正确地理解为什么会有这样的约束。表 tbl_student_class 中的 cname 存储的是每位学生的班级信息。

但需要注意的是,这里的 cname 只是每个学生的属性,并不是小组的属性,而 GROUP BY 又是聚合操作,操作的对象就是由多个学生组成的小组,因此,小组的属性只能是平均或者总和等统计性质的属性,如下图
在这里插入图片描述

询问每个学生的 cname 是可以的,但是询问由多个学生组成的小组的 cname 就没有意义了。对于小组来说,只有"一共多少学生"或者"最大学号是多少?"这样的问法才是有意义的。

强行将适用于个体的属性套用于团体之上,纯粹是一种分类错误;而 GROUP BY 的作用是将一个个元素划分成若干个子集,使用 GROUP BY 聚合之后,SQL 的操作对象便由 0 阶的"行"变为了 1 阶的"行的集合",此时,行的属性便不能使用了。

SQL 的世界其实是层级分明的等级社会,将低阶概念的属性用在高阶概念上会导致秩序的混乱,这是不允许的。此时我相信大家都明白:为什么聚合后不能再引用原表中的列 。

单元素集合也是集合

现在的集合论认为单元素集合是一种正常的集合。单元素集合和空集一样,主要是为了保持理论的完整性而定义的。因此对于以集合论为基础的 SQL 来说,当然也需要严格地区分元素和单元素集合。因此,元素 a 和集合 {a} 之间存在着非常醒目的层级差别。

a ≠ {a}

这两个层级的区别分别对应着 SQL 中的 WHERE 子句和 HAVING 子句的区别。WHERE 子句用于处理"行"这种 0 阶的对象,而 HAVING 子句用来处理"集合"这种 1 阶的对象。

总结

  1. SQL 严格区分层级,包括谓词逻辑中的层级(EXISTS),也包括集合论中的层级(GROUP BY);
  2. 有了层级区分,那么适用于个体上的属性就不适用于团体了,这也就是为什么聚合查询的 SELECT 子句中不能直接引用原表中的列的原因;
  3. 一般来说,单元素集合的属性和其唯一元素的属性是一样的。这种只包含一个元素的集合让人觉得似乎没有必要特意地当成集合来看待,但是为了保持理论的完整性,我们还是要严格区分元素和单元素集合;

最后

感谢你看到这里,我是不务正业的程序汪,一个java开发从业者,深耕行业六年了。

欢迎大家关注和转发文章,也欢迎大家关注我的公众号:程序员麦冬,每天都会分享java相关技术文章或行业资讯

<think>嗯,用户之前让我做了一个深度学习学习路径的流程图,分初期、进阶、强化三个阶段,现在他们想要我用小红书爆款博文的形式来介绍各个阶段。首先,我需要理解用户的需求变化。他们可能觉得之前的流程图虽然详细,但不够吸引人,或者想用更贴近大众、更具传播性的方式来呈现内容,适合社交媒体平台,比如小红书。 小红书上的爆款博文通常有几个特点:标题吸引眼球,使用表情符号和分段清晰,语言活泼亲切,多用emoji,内容结构通常是痛点+解决方案,或者步骤式、清单式,配合个人体验和实用建议。所以,我需要把原本的技术性内容转化为更轻松易懂、有亲和力的文案。 接下来,我需要回顾之前的三个阶段内容,将其拆解成适合小红书的格式。初期阶段的关键是基础构建,数学、编程、机器学习基础。这时候的读者可能是刚入门的小白,所以需要强调克服困难的方法和实用资源。进阶阶段是理论与实践结合,重点在神经网络和实战项目,用户可能已经有基础,需要鼓励他们动手做项目。强化阶段是专业深化,涉及高级主题和工程化,适合那些想深入或转行的人,需要突出职业发展和研究方向。 要注意每个阶段的标题要吸引人,比如使用“手把手教你入门”、“从理论到实战”、“进阶大神必备”这样的关键词。内容里要加入痛点,比如“数学劝退?不存在的!”来打消读者顾虑。还要使用表情符号来增加活泼感,比如🚀、💡、🔥等,让内容更生动。 另外,用户可能需要一些互动和鼓励,比如在结尾加上“评论区留言你的学习阶段”来增加互动。资源推荐部分要简洁,用符号分隔,方便读者快速获取信息。标签部分要包含相关话题,比如#深度学习、#AI学习路线,这样有助于内容被更多目标用户发现。 可能用户没明说的需求是希望内容不仅信息量大,还能激励读者,让他们觉得学习路径可行,有明确的步骤和资源支持。所以,在写作时要加入鼓励性的语言,比如“别怕枯燥,基础打牢才能起飞!”、“挑战高薪offer不是梦!”来增强动力。 最后,检查是否符合小红书的风格,确保每部分简短,段落清晰,使用符号和换行来提升可读性。避免技术术语过多,用更口语化的表达,比如“保姆级教程”、“小白也能看懂”来拉近和读者的距离。</think>以下是用 **小红书爆款博文风格** 整理的深度学习三阶段介绍,搭配表情包和互动话术,适合快速传播和种草👇 --- ### **🔥阶段一:小白入门 | 手把手教你从0到1搞懂深度学习!** **适合人群**:完全零基础,数学编程都头秃的萌新 **痛点**: ❌ 一看到“梯度下降”就懵圈? ❌ 代码跑不通,全网找bug到怀疑人生? **保姆级教程**: 1️⃣ **数学劝退?不存在的!** ▫️恶补3大核:线性代数(矩阵运算)+ 概率统计(贝叶斯公式)+ 微积分(梯度!) ▫️💡隐藏技巧:用《3Blue1Brown》动画课学,直观到跪! 2️⃣ **Python急救包** ▫️速通语法→狂练NumPy/Pandas数据处理 ▫️PyTorch框架抄近路:直接跟官方教程敲代码(GitHub搜“PyTorch Tutorial”) 3️⃣ **机器学习初体验** ▫️从“线性回归”开始→3天复现房价预测模型 ▫️🚀成就感拉满:Kaggle入门赛(泰坦尼克号生存预测)冲个青铜奖! **📢 互动**: “评论区留言你卡在哪个环节,揪3个宝子送代码debug大礼包!” --- ### **🚀阶段二:进阶突围 | 从调参侠到项目大佬的逆袭!** **适合人群**:会写代码但模型跑分像玄学的新手 **痛点**: ❌ CNN/RNN傻傻分不清? ❌ 调参调到深夜,结果还不如随机数? **逆袭指南**: 1️⃣ **神经网络拆解** ▫️搞懂前向传播+反向传播(链式法则yyds!) ▫️激活函数选ReLU还是Sigmoid?答案藏在损失函数曲线里! 2️⃣ **经典模型复刻** ▫️CNN必玩:用PyTorch实现ResNet,MNIST手写数字识别→秒变朋友圈学霸 ▫️NLP党狂喜:BERT+情感分析实战(某红书爆款评论分类拿来练手!) 3️⃣ **项目镀金法则** ▫️GitHub建仓库→放数据集+代码+README(HR面试直接甩链接!) ▫️💥隐藏加分项:用W&B记录超参数优化过程,可视化界面帅到离谱! **📢 暴言**: “别怕Kaggle卷不过!跟着TOP方案逐行解读,小白也能冲进前50%!” --- ### **💎阶段三:封神之路 | 挑战AI高薪offer的终极武器!** **适合人群**:想冲大厂/发顶会论文的硬核玩家 **痛点**: ❌ 模型部署一脸懵?工业级项目无从下手? ❌ 论文读不懂,复现结果像开盲盒? **王炸组合**: 1️⃣ **高阶模型实战** ▫️GAN生成二次元老婆(附教程:StyleGAN2+动漫数据集) ▫️强化学习训练AI玩《王者荣耀》(GitHub搜“王者AI开源项目”) 2️⃣ **工程化秘籍** ▫️模型压缩:知识蒸馏让ResNet瘦身90%! ▫️部署实战:把YOLO检测模型塞进手机(Android Studio+TensorFlow Lite) 3️⃣ **学术/工业二选一** ▫️科研党:精读10篇顶会论文→复现→魔改创新点(CVPR/ICLR冲就完事) ▫️就业党:刷爆LeetCode算法题+开发LLM落地项目(挑战年薪50W+!) **📢 总结**: “发顶会的大佬也曾是调参侠,你和大厂offer的距离,只差一个果断的Star⭐!” --- ### **🌟资源福利区** ``` 【数学基础】《深度学习数学简易入门》电子书👉评论区扣“666” 【代码模板】GAN+Transformer项目合集👉关注后私信“DL”自动发送 【避坑指南】新手必看的10大误区文档👉戳主页群聊领取 ``` **#深度学习 #AI学习路线 #Kaggle竞赛 #转行人工智能 #研究生日常** --- **互动话术**: “你的深度学习修炼到哪个阶段了? A. 刚入坑瑟瑟发抖 B. 调参侠正在渡劫 C. 项目大佬求带飞 评论区蹲校友组队!”
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值