当前位置: 首页 > news >正文

SQL入门:集合运算实战指南

在 SQL 数据查询中,集合运算用于对多个数据集进行 “组合、对比、筛选”,核心包括合并(UNION/UNION ALL)交集(INTERSECT)差集(EXCEPT/MINUS),而补集需通过 “全量集合 - 目标集合” 的差集实现。这些运算广泛用于多表数据整合(如合并多平台订单)、数据对比(如找出跨表共同用户)等场景。本文从基础概念到实战场景,全面解析五种集合运算的用法、规则及数据库差异。

一、集合运算的前提:数据兼容性

所有集合运算需满足数据集结构完全兼容,否则会报错,核心要求:

  1. 字段数量相同:两个数据集的返回字段个数必须一致(如 A 返回 2 个字段,B 也需返回 2 个字段);
  2. 字段类型兼容:对应位置的字段数据类型需可隐式转换(如INTDECIMAL兼容,VARCHARTEXT兼容);
  3. 字段顺序一致:对比时按 “第 1 个字段 vs 第 1 个字段”“第 2 个字段 vs 第 2 个字段” 匹配,与字段名无关(建议字段名一致,避免混淆)。

示例:表 A(user_id INT, username VARCHAR)与表 B(user_id INT, username VARCHAR)可运算;若表 B 是(username VARCHAR, user_id INT),需调整字段顺序后再运算。

二、合并运算:UNION 与 UNION ALL(合并多个集合)

合并运算用于将 “多个结构兼容的数据集” 合并为一个结果集,核心区别是是否去重,是多表数据整合的常用工具。

1. UNION:合并并自动去重

(1)核心概念

UNION用于合并两个或多个数据集,并自动去除重复记录,最终返回 “所有不重复的记录”。可理解为:A UNION B = 所有在 A 中或在 B 中的记录(无重复)。

(2)标准语法
-- 合并两个数据集,自动去重
SELECT 字段列表 FROM 表A [WHERE 条件A]
UNION
SELECT 字段列表 FROM 表B [WHERE 条件B]
[ORDER BY 排序字段]; -- 可选,对最终结果排序
(3)关键规则
  • 自动去重:无论原数据集是否有重复,合并后仅保留唯一记录;
  • 字段匹配:仅对比 “对应位置的字段值”,完全相同的记录才视为重复;
  • 性能消耗:去重需额外计算(如排序对比),大数据量下效率低于UNION ALL
(4)实例:合并多平台订单

假设有两个平台的订单表:

  • app_orders(手机端订单):order_id INT, user_id INT, amount DECIMAL
  • web_orders(网页端订单):order_id INT, user_id INT, amount DECIMAL

需求:合并两个平台的订单,去除重复记录(如同一订单被误录到两个表)。

-- 合并并去重
SELECT order_id, user_id, amount FROM app_orders
UNION
SELECT order_id, user_id, amount FROM web_orders
ORDER BY order_id; -- 按订单ID排序
  • 结果:若app_ordersweb_orders有相同order_id的记录,仅保留一条。

2. UNION ALL:合并但不去重

(1)核心概念

UNION ALL用于合并两个或多个数据集,不进行去重,直接保留所有记录(包括重复项)。可理解为:A UNION ALL B = 所有在 A 中的记录 + 所有在 B 中的记录(保留重复)。

(2)标准语法
-- 合并两个数据集,保留重复
SELECT 字段列表 FROM 表A [WHERE 条件A]
UNION ALL
SELECT 字段列表 FROM 表B [WHERE 条件B]
[ORDER BY 排序字段];
3)关键规则
  • 不去重:原数据中的重复记录会全部保留(如 A 中有 2 条相同记录,B 中有 3 条,结果共 5 条);
  • 性能优势:无需去重计算,效率远高于UNION,是大数据量合并的首选;
  • 适用场景:明确无重复记录,或需保留重复记录(如统计总订单量)。
(4)实例:统计多平台总订单量

需求:合并两个平台的订单,统计总订单数(需保留所有订单,包括重复录入的)。

-- 合并并保留所有记录,统计总数
SELECT COUNT(*) AS 总订单量
FROM (SELECT order_id FROM app_ordersUNION ALL  -- 不去重,确保所有订单被统计SELECT order_id FROM web_orders
) AS all_orders;
  • 逻辑:子查询合并所有订单(含重复),外层COUNT(*)统计总数量,结果更准确。

3. UNION 与 UNION ALL 的核心对比

对比维度UNIONUNION ALL
去重行为自动去重不去重,保留所有记录
性能低(需排序去重)高(直接合并,无额外计算)
结果行数≤ 两个数据集行数之和= 两个数据集行数之和
适用场景需去重的合并(如整合唯一记录)无需去重的合并(如统计总量)

核心建议:优先使用UNION ALL(效率高),仅当明确需要去重时才用UNION

三、交集运算:INTERSECT(取共同记录)

1. 核心概念

INTERSECT用于获取 “同时存在于两个数据集的记录”,即 “既在 A 中,又在 B 中” 的记录,可理解为 “逻辑与”。语法:A INTERSECT B = 所有在 A 且在 B 中的记录(自动去重)。

2. 标准语法

SELECT 字段列表 FROM 表A [WHERE 条件A]
INTERSECT
SELECT 字段列表 FROM 表B [WHERE 条件B]
[ORDER BY 排序字段];

3. 实例:找出跨平台下单的用户

需求:找出 “同时在手机端和网页端下单” 的用户(即两个订单表的共同用户)。

-- 交集:同时在app和web下单的用户
SELECT DISTINCT user_id FROM app_orders
INTERSECT
SELECT DISTINCT user_id FROM web_orders;
  • 结果:仅返回在两个表中都存在的user_id,自动去重(即使同一用户下多笔订单,也仅出现一次)。

四、差集运算:EXCEPT 与 MINUS(取独有记录)

差集用于获取 “在第一个数据集(A)中,但不在第二个数据集(B)中的记录”,核心是 “逻辑差”,但需注意顺序敏感性A-BB-A)。不同数据库对差集的关键字不同:标准 SQL 用EXCEPT,Oracle 用MINUS

1. EXCEPT(标准 SQL)

(1)核心概念

EXCEPT返回 “在 A 中但不在 B 中的记录”,自动去重。语法:A EXCEPT B = 所有在 A 中且不在 B 中的记录。

(2)实例:找出 “仅手机端下单的用户”

需求:找出 “在app_orders中但不在web_orders中” 的用户(仅手机端活跃用户)。

-- 差集:仅app下单的用户
SELECT DISTINCT user_id FROM app_orders
EXCEPT
SELECT DISTINCT user_id FROM web_orders;

2. MINUS(Oracle 专属)

(1)核心概念

MINUS是 Oracle 对差集的实现,功能与EXCEPT完全一致,仅关键字不同。语法:A MINUS B = 所有在 A 中且不在 B 中的记录(自动去重)。

(2)实例:Oracle 中找出未下单的注册用户

假设有users(注册用户)和orders(订单)表,需求:找出 “已注册但未下单” 的用户。

-- Oracle语法:用MINUS实现差集
SELECT user_id FROM users
MINUS
SELECT DISTINCT user_id FROM orders;

3. EXCEPT/MINUS 的关键规则

  • 顺序敏感A EXCEPT B返回 A 的独有记录,B EXCEPT A返回 B 的独有记录,顺序颠倒结果完全不同;
  • 自动去重:结果会去除重复记录(即使 A 中有重复的独有记录,结果也仅保留一条);
  • NULL 处理NULLNULL视为不相等(因 NULL 表示 “未知”,无法确定是否相同)。

五、补集运算:全量集合 - 目标集合(间接实现)

标准 SQL 未直接定义 “补集” 运算符,但补集的本质是 “全量集合中除去目标集合的部分”,可通过 “全量集合EXCEPT目标集合” 间接实现。

1. 核心概念

补集 = 全量集合 - 目标集合,即 “所有符合全量范围,但不符合目标条件” 的记录。前提:需先明确 “全量集合”(如 “所有用户”“所有商品”)。

2. 实现语法

-- 补集 = 全量集合 EXCEPT 目标集合
SELECT 字段列表 FROM 全量表 [WHERE 全量条件]  -- 定义全量范围
EXCEPT
SELECT 字段列表 FROM 目标表 [WHERE 目标条件];  -- 定义需排除的范围

3. 实例:找出未实名认证的用户

假设:

  • 全量集合:users表中所有注册用户;
  • 目标集合:user_cert表中已实名认证的用户。

需求:找出 “所有注册用户中未实名认证” 的用户。

-- 补集:未实名认证用户 = 所有用户 - 已认证用户
SELECT user_id, username FROM users
EXCEPT
SELECT u.user_id, u.username 
FROM users u
JOIN user_cert c ON u.user_id = c.user_id
WHERE c.cert_status = '已认证';

六、主流数据库的集合运算兼容性

不同数据库对集合运算的支持程度不同,核心差异如下表,新手需重点关注:

数据库UNIONUNION ALLINTERSECTEXCEPTMINUS
MySQL 8.0+×
MySQL 5.7 及以下××××
PostgreSQL×
SQL Server×
Oracle×

避坑指南:

  1. MySQL 5.7 及以下:无INTERSECT/EXCEPT,需用INNER JOIN(交集)、LEFT JOIN + IS NULL(差集)替代;
  2. Oracle:差集用MINUS,而非EXCEPT
  3. 保留重复记录:若需保留原数据中的重复项(如 “重复的独有记录”),PostgreSQL/SQL Server 可用EXCEPT ALL,其他数据库需用JOIN实现。

七、实战场景:集合运算的综合应用

场景 1:多表数据整合与统计

需求:有 3 个区域的销售表(north_salessouth_saleseast_sales),需合并所有销售数据,统计 “总销售额” 和 “唯一产品数”。

WITH all_sales AS (-- 用UNION ALL合并所有销售记录(保留重复,确保销售额准确)SELECT product_id, amount FROM north_salesUNION ALLSELECT product_id, amount FROM south_salesUNION ALLSELECT product_id, amount FROM east_sales
)
SELECTSUM(amount) AS 总销售额,          -- 统计总销售额(保留重复,结果准确)COUNT(DISTINCT product_id) AS 唯一产品数 -- 统计唯一产品(去重)
FROM all_sales;

场景 2:用户行为分析(新增 vs 留存)

需求:已知 “2024 年 1 月注册用户” 和 “2024 年 2 月下单用户”,需分析:

  1. 1 月注册且 2 月下单的用户(留存用户);
  2. 1 月注册但 2 月未下单的用户(流失用户)。
-- 定义两个基础数据集
WITH jan_reg_users AS (  -- 1月注册用户(全量)SELECT user_id FROM usersWHERE register_time BETWEEN '2024-01-01' AND '2024-01-31'
),
feb_order_users AS (  -- 2月下单用户(目标)SELECT DISTINCT user_id FROM ordersWHERE create_time BETWEEN '2024-02-01' AND '2024-02-29'
)
-- 1. 留存用户(交集)
SELECT user_id AS 留存用户 FROM jan_reg_users
INTERSECT
SELECT user_id FROM feb_order_users;-- 2. 流失用户(差集)
SELECT user_id AS 流失用户 FROM jan_reg_users
EXCEPT
SELECT user_id FROM feb_order_users;

场景 3:数据清洗(删除无效记录)

需求:orders表中有部分user_idusers表中不存在(无效订单),找出并删除这些无效订单。

-- 步骤1:用差集找出无效订单(orders中无对应user_id的记录)
SELECT order_id FROM orders
EXCEPT
SELECT o.order_id 
FROM orders o
JOIN users u ON o.user_id = u.user_id;-- 步骤2:删除无效订单(谨慎操作,建议先查询确认)
DELETE FROM orders
WHERE order_id IN (SELECT order_id FROM ordersEXCEPTSELECT o.order_id FROM orders oJOIN users u ON o.user_id = u.user_id
);

八、常见误区与避坑指南

1. 误区 1:忽略字段兼容性(数量 / 类型 / 顺序)

问题:两个数据集字段数量不同,或类型 / 顺序不匹配,导致运算报错。

-- 错误示例1:字段数量不同(A返回2个字段,B返回1个)
SELECT user_id, username FROM users
UNION
SELECT user_id FROM orders; -- 报错:字段数量不匹配-- 错误示例2:字段类型不兼容(A的amount是INT,B的amount是VARCHAR)
SELECT order_id, amount FROM app_orders
UNION
SELECT order_id, amount FROM web_orders; -- 报错:类型不兼容

解决:确保所有数据集的字段数量、类型、顺序完全一致(可通过CAST转换类型,调整字段顺序)。

2. 误区 2:滥用 UNION(无需去重时用了 UNION)

问题:明确无重复记录,却用UNION(而非UNION ALL),导致性能浪费。

-- 需求:合并两个无重复的区域销售表,统计总金额
-- 错误:用UNION去重(多余计算,效率低)
SELECT amount FROM north_sales
UNION
SELECT amount FROM south_sales;-- 正确:用UNION ALL(无多余计算,效率高)
SELECT amount FROM north_sales
UNION ALL
SELECT amount FROM south_sales;

解决:优先用UNION ALL,仅当需去重时才用UNION

3. 误区 3:混淆差集顺序(A-B vs B-A)

问题:误将B EXCEPT A当作A EXCEPT B,导致结果完全相反。

-- 需求:找出“1月注册但2月未下单”的用户(A=1月注册,B=2月下单)
-- 错误:B-A=2月下单但1月未注册的用户(与需求相反)
SELECT user_id FROM feb_order_users
EXCEPT
SELECT user_id FROM jan_reg_users;

解决:明确 “源集合(需保留的基础集合)” 和 “排除集合(需去除的集合)”,按 “源集合 EXCEPT 排除集合” 编写。

4. 误区 4:依赖集合运算的默认排序

问题:部分数据库(如 SQL Server)对集合运算结果默认排序,若业务需固定顺序,未显式加ORDER BY

-- 风险:结果顺序可能随数据库版本或数据量变化
SELECT user_id FROM users EXCEPT SELECT user_id FROM orders;-- 安全:显式指定排序,确保结果稳定
SELECT user_id FROM users EXCEPT SELECT user_id FROM orders
ORDER BY user_id;

解决:无论数据库是否默认排序,均显式添加ORDER BY,避免结果顺序异常。

九、总结

集合运算是 SQL 处理多数据集的核心工具,不同运算的适用场景和核心差异如下:

运算类型关键字 / 实现方式核心作用去重行为适用场景
合并UNION合并并去重自动去重整合唯一记录(如跨表唯一用户)
合并UNION ALL合并不去重不去重统计总量、保留重复记录(如多平台总订单)
交集INTERSECT取两个集合的共同记录自动去重找出跨表共同数据(如跨平台下单用户)
差集EXCEPT(标准)/MINUS(Oracle)取 A 的独有记录(A-B)自动去重找出单表独有数据(如未下单的注册用户)
补集全量集合 EXCEPT 目标集合取全量中除去目标的记录自动去重找出全量中的例外数据(如未认证用户)

实际开发中,需根据业务需求选择合适的运算:

  • 合并数据优先用UNION ALL(效率高);
  • 对比共同数据用INTERSECT
  • 对比独有数据用EXCEPT(或 Oracle 的MINUS);
  • 全量例外数据用 “全量集合 - 目标集合” 的补集实现。

同时,需注意数据库兼容性差异,避免语法错误;大数据量场景下,优先用JOIN替代INTERSECT/EXCEPT(效率更高),确保查询性能。

http://www.dtcms.com/a/477681.html

相关文章:

  • Docker 网络类型与容器通信
  • Oracle 21C 部署ogg踩过的坑
  • vue3 中播放.flv视频
  • Oracle AWR报告中Load Profile源码
  • 电子商务网站开发的任务书企业文化心得体会
  • 溧阳网站开发kindeditor代码高亮 wordpress
  • CSS常见问题
  • nginx 负载均衡配置
  • 原生 iOS 开发全流程实战,Swift 技术栈、工程结构、自动化上传与上架发布指南
  • Apache开源许可证:深度解析与实践指南
  • Python Web开发——HTTP协议简介
  • 病理切片可解释性分析-细胞类型、核形态与细胞间相互作用
  • 【C语言】杨辉三角:数学之美与编程实现的完美结合
  • 盐城网站建设策划方案杭州企业求网站建设
  • 基于.NET Framework 4.0的串口通信
  • Mybatis Log Free插件使用
  • asp网站可运行jsp吗专做立体化的网站
  • angie未生成coredump文件
  • 【leetcode刷题007】leetcode116、117
  • 南昌网站推广¥做下拉去118cr如何做实验室网站
  • 从0开始搭建Vue3项目
  • 【知识】Linux将多条指令合并为一条执行
  • 将 MapReduce 程序打成 JAR 包并在 Linux 虚拟机的 Hadoop 集群上运行
  • 06_Miniconda零基础安装配置指南:打造高效的Python开发环境
  • 量子计算与AI融合:材料科学新突破!!
  • 徐州网站建设方案咨询外贸牛
  • 顶级 AI 模型横评:智能、性能与价格等方面全面对比
  • Vuex 详细用法(Vue2 和 Vue3)
  • 注册公司网站如何注册黑龙江公共资源交易网官网
  • 如何将iPhone上的HEIF图像下载到电脑