SQL 分组查询中的非聚合列要求及实例解析
在 SQL 查询中,当我们对数据进行分组时,通常会用到 GROUP BY
子句。SQL 标准要求:所有非聚合列(即没有使用聚合函数的列)都必须出现在 GROUP BY
子句中,或者是聚合函数的结果。这篇文章通过一个实例来说明这个规则的原因及如何正确书写查询语句。
场景描述
我们有三张表:Customers
(顾客信息)、Orders
(订单信息)、OrderItems
(订单项信息),它们分别包含以下字段:
-
Customers 表
cust_id
:顾客 IDcust_name
:顾客名称
-
Orders 表
order_num
:订单号cust_id
:顾客 ID(与Customers
表关联)
-
OrderItems 表
order_num
:订单号(与Orders
表关联)quantity
:商品数量item_price
:商品单价
需求是返回每个顾客的 cust_name
和与之关联的订单号 order_num
,以及每个订单的总价 OrderTotal
,并按顾客名称和订单号进行排序。
数据示例
Customers
表:
cust_id | cust_name |
---|---|
cust10 | andy |
cust1 | ben |
cust2 | tony |
cust22 | tom |
cust221 | an |
cust2217 | hex |
Orders
表:
order_num | cust_id |
---|---|
a1 | cust10 |
a2 | cust1 |
a3 | cust2 |
a4 | cust22 |
a5 | cust221 |
a7 | cust2217 |
OrderItems
表:
order_num | quantity | item_price |
---|---|---|
a1 | 1000 | 10 |
a2 | 200 | 10 |
a3 | 10 | 15 |
a4 | 25 | 50 |
a5 | 15 | 25 |
a7 | 7 | 7 |
正确的 SQL 查询
为了实现需求,我们需要将三张表进行连接,并计算每个订单的总价。计算总价的方法是将 quantity
和 item_price
相乘,并对每个订单的所有项进行求和。最终的 SQL 查询如下:
SELECT c.cust_name, o.order_num, SUM(quantity * item_price) AS OrderTotal
FROM Customers c
JOIN Orders o ON c.cust_id = o.cust_id
JOIN OrderItems oi ON o.order_num = oi.order_num
GROUP BY c.cust_name, o.order_num
ORDER BY c.cust_name, o.order_num;
查询解析
- 连接三张表:通过
cust_id
将Customers
和Orders
表连接,通过order_num
将Orders
和OrderItems
表连接,获取顾客、订单和订单项的完整信息。 - 计算订单总价:使用
SUM(quantity * item_price)
计算每个订单的总金额。 - 分组与排序:
GROUP BY c.cust_name, o.order_num
按顾客名称和订单号分组,使每个分组对应一个顾客的一个订单,从而计算出每个订单的总金额。ORDER BY c.cust_name, o.order_num
对结果按顾客名称和订单号排序。
查询结果
查询将返回顾客的姓名、订单号以及该订单的总价,并按顾客名称和订单号进行排序:
cust_name | order_num | OrderTotal |
---|---|---|
andy | a1 | 10000 |
ben | a2 | 2000 |
tony | a3 | 150 |
tom | a4 | 1250 |
an | a5 | 375 |
hex | a7 | 49 |
错误示例及解析
一个常见的错误查询示例如下:
SELECT c.cust_name, o.order_num, SUM(quantity * item_price) AS OrderTotal
FROM Customers c, Orders o, OrderItems oi
WHERE c.cust_id = o.cust_id AND o.order_num = oi.order_num
GROUP BY c.cust_name
ORDER BY c.cust_name, o.order_num;
错误原因:
- 这个查询中只在
GROUP BY
子句中使用了cust_name
,但order_num
未包含在GROUP BY
中,导致 SQL 无法确定如何处理order_num
。 - 在 SQL 中,
GROUP BY
子句中的列需要包含所有非聚合列(即未被聚合函数包裹的列)。否则,SQL 将无法知道如何处理这些非聚合列的值,从而导致错误。
1. 什么是聚合列和非聚合列?
-
聚合列:指的是在
SELECT
语句中通过聚合函数(如SUM()
、AVG()
、MAX()
、MIN()
、COUNT()
等)计算出的列。它们通常是针对分组数据进行的汇总统计,例如求某个分组中的所有值的和、平均值等。 -
非聚合列:在
SELECT
语句中未使用聚合函数的列,即直接显示的列,没有进行任何聚合操作。这些列通常用于显示某个分组的特定属性。
2. 为什么非聚合列必须出现在 GROUP BY
子句中,或者是聚合函数的结果?
在分组查询中,SQL 标准要求所有出现在 SELECT
语句中的列要么是聚合函数的结果,要么出现在 GROUP BY
子句中。其原因如下:
-
确定性原则:在分组查询中,每个分组内可能包含多行数据,如果我们在
SELECT
中选择了一个非聚合列,但没有将其包含在GROUP BY
子句中,SQL 不知道应该选取哪个具体的值,因为每个分组中的该列值可能不同,这会导致不确定性。 -
保证数据一致性:当一个查询返回结果时,用户会希望每一行数据都是确定的、清晰的。如果没有将非聚合列放入
GROUP BY
,则会出现难以理解的数据,可能导致数据含义不清或误导。
举例说明
假设有一个 sales
表,结构如下:
id | region | sales_amount |
---|---|---|
1 | East | 200 |
2 | East | 150 |
3 | West | 300 |
4 | West | 250 |
我们希望按 region
进行分组,求每个地区的总销售额。正确的 SQL 查询应该是:
SELECT region, SUM(sales_amount) AS total_sales
FROM sales
GROUP BY region;
输出结果:
region | total_sales |
---|---|
East | 350 |
West | 550 |
在这个例子中,SUM(sales_amount)
是一个聚合列,而 region
是一个非聚合列。因为 region
出现在 GROUP BY
子句中,SQL 知道需要按 region
分组,然后对每组的数据进行汇总。
错误的写法
如果我们写成如下形式,将 id
(非聚合列)放在 SELECT
语句中,却不出现在 GROUP BY
中:
SELECT id, region, SUM(sales_amount) AS total_sales
FROM sales
GROUP BY region;
这样 SQL 会报错,因为每个 region
可能包含多行不同的 id
,系统无法确定返回哪个 id
。这就是 SQL 标准所要避免的“不确定性”问题。
如何解决?
如果确实需要某个非聚合列出现在查询结果中,那么我们有两种选择:
-
将非聚合列加入
GROUP BY
子句中:如果id
是我们希望按某个粒度分组的属性,可以将其加入GROUP BY
。SELECT id, region, SUM(sales_amount) AS total_sales FROM sales GROUP BY id, region;
-
使用聚合函数处理该列:如果想得到某个分组内的特定
id
,可以使用聚合函数,如MAX(id)
或MIN(id)
,以明确返回的值。SELECT MAX(id) AS example_id, region, SUM(sales_amount) AS total_sales FROM sales GROUP BY region;
总结
在 SQL 中,当我们进行分组查询时,所有出现在 SELECT
语句中的非聚合列都必须出现在 GROUP BY
子句中,或者使用聚合函数包裹。这样做的原因是,GROUP BY
子句能确保查询结果是确定的、清晰的。否则,SQL 将无法理解如何处理这些非聚合列,可能导致错误。
相关文章:
SQL 分组查询中的非聚合列要求及实例解析
在 SQL 查询中,当我们对数据进行分组时,通常会用到 GROUP BY 子句。SQL 标准要求:所有非聚合列(即没有使用聚合函数的列)都必须出现在 GROUP BY 子句中,或者是聚合函数的结果。这篇文章通过一个实例来说明这…...
Unity中实现战斗帧同步的高级技术
一、帧同步的基本原理 帧同步(Frame Synchronization)在网络游戏中指的是在每一帧上保证所有玩家所看到的游戏状态一致,而不是每个玩家单独计算自己的状态。实现帧同步通常需要每个客户端仅发送用户输入到服务器,并由服务器进行全…...
Qt 正则表达式提取文件中的 USB 设备 ID
Qt 正则表达式提取文件中的 USB 设备 ID flyfish 文档内容 Bus: 001 Device: 001 Description: 1d6b:0002 Linux Foundation 2.0 root hub Bus: 002 Device: 003 Description: 0e0f:0002 , Inc. USB Hub Bus: 002 Device: 002 Description: 0e0f:0003 , Inc. Mouse Bus: 002…...
使用 Python 和 OpenCV 实现摄像头人脸检测并截图
概述 在现代应用中,人脸检测是一项非常重要的技术,广泛应用于安全监控、身份验证等领域。本文将详细介绍如何使用 Python 和 OpenCV 库实现摄像头人脸检测并截图,并通过具体的代码示例来展示整个过程。 环境准备 在开始编写代码之前&#…...

【二叉搜素树】——LeetCode二叉树问题集锦:6个实用题目和解题思路
文章目录 计算布尔二叉树的值求根节点到叶节点的数字之和二叉树剪枝验证二叉搜索树二叉搜索树中第K小的元素二叉树的所有路径 计算布尔二叉树的值 解题思路: 这是一个二叉树的布尔评估问题。树的每个节点包含一个值,其中叶子节点值为 0 或 1࿰…...
【计算机视觉】FusionGAN
1. FusionGAN论文阅读 abreheret/FusionGAN: Pytorch implementation of "Generating a Fusion Image: One’s Identity and Another’s Shape" 1.1. WHY 在现实世界中,将对象或人物转换为期望的形状是一种常用技术,但现有的图像翻译方法在处理身份和形状时存在…...
问:SQL优化,七条实践总结?
SQL语句优化是数据库性能调优的重要部分,通过合理的优化可以显著提升查询速度和系统性能。文章总结几种常见SQL语句优化方法。 1. 优化Where子句的顺序 原则:表之间的连接条件应写在其他Where条件之前,能够过滤掉最大数量记录的条件应优先写…...

unity单例模式的不同声明(待完善
总结: 这段代码实现了一个泛型单例模式(Singleton Pattern),用于确保某个类(由泛型参数 T 指定)在整个应用程序中只有一个实例,并且在第一次访问时才创建该实例。该模式保证了该实例的全局唯一…...

大模型在蓝鲸运维体系应用——蓝鲸运维开发智能助手
本文来自腾讯蓝鲸智云社区用户: CanWay 背景 1、运维转型背景 蓝鲸平台从诞生之初,就一直在不遗余力地推动运维转型,让运维团队可以通过一体化PaaS平台,快速编写脚本,编排流程,开发运维工具,从被动地提供…...
vue2,vue3响应式的理解
vue2的话主要使用的是defineProperty对已有属性添加get,set,从而完成对数据的响应式控制,但每次需要for循环对属性进行遍历 function DefineReactive(target, key, value) {//存在多层嵌套的objectObserver(value);Object.defineReactive(target, key, {get() {retu…...

群控系统服务端开发模式-应用开发-前端退出功能
我们从未登录一直到退出,现在已经登录到操作,现在完成退出。退出有两种情况下会退出:第一种情况下是手动点击退出按钮,第二种情况下是登录过期时间到了自动退出的。 一、手动退出 因退出及个人信息页面都在公有页面,所…...

Web入门
Spring 官网:Spring | Home Spring是一个开源的Java企业级应用开发框架。Spring的主要目的是使Java EE(Java Platform, Enterprise Edition)开发更容易,并且通过提供一系列丰富的库和接口来促进良好编程实践,是…...

基于SpringBoot网上超市的设计与实现录像
基于SpringBoot网上超市的设计与实现录像 SpringBoot网上超市的设计与实现录像...

python爬虫(二)爬取国家博物馆的信息
import requests from bs4 import BeautifulSoup# 起始网址 url https://www.chnmuseum.cn/zx/xingnew/index_1.shtml # 用于存储所有数据 all_data [] page 1 global_index 1 # 定义全局序号变量并初始化为1 while True:html_url requests.get(url).textif requests.get…...
【mysql的当前读和快照读】
在MySQL中,尤其是InnoDB存储引擎中,读操作主要分为两种:当前读(Current Read)和快照读(Snapshot Read) 当前读 当前读每次读取的都是当前最新的数据。这种读操作在读取数据时不允许其他事务对这…...

[CKS] Audit Log Policy
最近准备花一周的时间准备CKS考试,在准备考试中发现有一个题目关于audit policy的题目。 专栏其他文章: [CKS] Create/Read/Mount a Secret in K8S-CSDN博客[CKS] Audit Log Policy-CSDN博客 -[CKS] 利用falco进行容器日志捕捉和安全监控-CSDN博客[CKS] K8S Netw…...

【Linux】-学习笔记03
第十一章-管理Linux软件包和进程 1.源码下载安装软件 1.1概念 源码文件:程序编写者使用C或C等语言编写的原始代码文本文件 源码文件使用.tar.gz或.tar.bz2打包成压缩文件 1.2特点 源码包可移植性好,与待安装软件的工作环境依赖性不大 由于有编译过程…...
Leetcode热题100-32 最长有效括号
Leetcode热题100-32 最长有效括号 1. 题目描述2. 解题思路动态规划栈解法 3. 代码实现动态规划栈解法 1. 题目描述 32 最长有效括号 2. 解题思路 动态规划 定义状态: 设 dp[i] 表示以位置 i 结尾的最长有效括号子串的长度。 状态转移方程: 遍历字符…...

【大数据学习 | HBASE】hbase的读数据流程与hbase读取数据
1. hbase的读数据流程 在解析读取流程之前我们还需要知道两个功能性的组件和HFIle的格式信息 HFILE 存储在hdfs中的hbase文件,这个文件中会存在hbase中的数据以kv类型显示,同时还会存在hbase的元数据信息,包括整个hfile文件的索引大小&…...

A027-基于Spring Boot的农事管理系统
🙊作者简介:在校研究生,拥有计算机专业的研究生开发团队,分享技术代码帮助学生学习,独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取,记得注明来意哦~🌹 赠送计算机毕业设计600…...
服务器硬防的应用场景都有哪些?
服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...

PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...
【生成模型】视频生成论文调研
工作清单 上游应用方向:控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...

基于SpringBoot在线拍卖系统的设计和实现
摘 要 随着社会的发展,社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统,主要的模块包括管理员;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...

【C++进阶篇】智能指针
C内存管理终极指南:智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...

宇树科技,改名了!
提到国内具身智能和机器人领域的代表企业,那宇树科技(Unitree)必须名列其榜。 最近,宇树科技的一项新变动消息在业界引发了不少关注和讨论,即: 宇树向其合作伙伴发布了一封公司名称变更函称,因…...

C# 表达式和运算符(求值顺序)
求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如,已知表达式3*52,依照子表达式的求值顺序,有两种可能的结果,如图9-3所示。 如果乘法先执行,结果是17。如果5…...