五种主流数据库:集合运算

关系型数据库中的表与集合理论中的集合类似,表是由行(记录)组成的集合。因此,SQL 支持基于数据行的各种集合运算,包括并集运算(Union)、交集运算(Intersect)和差集运算(Except)。它们都可以将两个查询的结果集合并成一个结果集,但是合并的规则各不相同。

本文比较了五种主流数据库实现的集合运算,包括 MySQL、Oracle、SQL Server、PostgreSQL 以及 SQLite。

功能MySQLOracleSQL ServerPostgreSQLSQLite
INTERSECT✔️✔️✔️✔️✔️
INTERSECT ALL✔️✔️✔️
UNION✔️✔️✔️✔️✔️
UNION ALL✔️✔️✔️✔️✔️
EXCEPT✔️✔️✔️✔️✔️
EXCEPT ALL✔️✔️✔️

执行 SQL 集合运算时,集合操作中的两个查询结果需要满足以下条件:

  • 两个查询结果集中字段的数量必须相同。
  • 两个查询结果集中对应字段的类型必须匹配或兼容。SQLite 使用动态数据类型,不要求字段类型匹配或兼容。

也就是说,参与运算的两个查询结果集的字段结构必须相同。如果一个查询返回 2 个字段,另一个查询返回 3 个字段,肯定无法进行合并。如果一个查询返回数字类型的字段,另一个查询返回字符类型的字段,通常也无法进行合并;不过,某些数据库(例如 MySQL)可能会尝试执行隐式的类型转换。

交集求同

SQL 交集运算的运算符是 INTERSECT,它可以用于获取两个查询结果集中的共同部分,也就是同时出现在第一个查询结果集和第二个查询结果集中的数据,如下图所示:

在这里插入图片描述
图 中的 1 和 2 是两个查询结果集中都存在的元素,因此交集运算的结果只包含 1 和 2。

SQL 交集运算的语法如下:

SELECT column1, column2, ...
FROM table1
INTERSECT [DISTINCT | ALL]
SELECT col1, col2, ...
FROM table2;

其中,DISTINCT 表示对合并后的结果集进行去重操作,只保留不重复的记录。ALL 表示保留合并结果中的重复记录。如果省略,默认值为 DISTINCT。

注意:MySQL 8.0开始支持 INTERSECT 运算符以及 ALL 选项。PostgreSQL 支持完整的 DISTINCT 和 ALL 选项,Oracle 21c 开始支持 ALL 选项,SQL Server 以及 SQLite 支持简写的 INTERSECT。

我们首先创建两个简单的测试表 t_set1 和 t_set2。

CREATE TABLE t_set1
(
 id INTEGER,
 name VARCHAR(10)
);
INSERT INTO t_set1 VALUES (1, 'apple');
INSERT INTO t_set1 VALUES (2, 'banana');
INSERT INTO t_set1 VALUES (3, 'orange');
CREATE TABLE t_set2
(
 id INTEGER,
 name VARCHAR(10)
);
INSERT INTO t_set2 VALUES (1, 'apple');
INSERT INTO t_set2 VALUES (2, 'banana');
INSERT INTO t_set2 VALUES (4, 'pear');

然后使用以下语句查找两个表中的共同记录。

SELECT id, name
FROM t_set1
INTERSECT
SELECT id, name
FROM t_set2;

查询返回的结果如下:

id|name 
--|------
 1|apple 
 2|banana

其中,“apple”和“banana”是两个表中的共同数据。

以上示例中两个 SELECT 语句返回的列名都是 id 和 name,因此最终结果返回的列表也是 id 和 name。如果两个语句返回的列名不同,最终结果使用第一个语句返回的列名。

通常来说,交集运算都可以改写为等价的内连接查询。上面的查询语句可以改写为下面这样:

SELECT DISTINCT t1.id, t1.name
FROM t_set1 t1
JOIN t_set2 t2
ON (t2.id = t1.id AND t2.name = t1.name);

注意,SELECT 列表中返回的全部字段(id 和 name)都必须作为连接查询的条件。

前文我们提到过,使用 SQL 集合运算的前提是,参与集合运算的两个查询结果集必须包含相同数量的字段,并且对应字段的数据类型必须匹配。因此,以下两个示例都会返回错误:

SELECT id
FROM t_set1
INTERSECT
SELECT id, name
FROM t_set2;

SELECT id, id
FROM t_set1
INTERSECT
SELECT id, name
FROM t_set2;

在第一个示例中,两个 SELECT 语句返回的字段数量不相同;在第二个示例中,两个 SELECT 语句返回的字段数据类型不一致。对于第二个查询示例,SQLite 不会返回错误。

并集存异

SQL 并集运算的运算符是 UNION,它可以用于计算两个查询结果集的相加,返回出现在第一个查询结果集或者第二个查询结果集中的数据,如下图所示。

在这里插入图片描述
图中的 1 和 2 是两个查询结果集中都存在的元素,不过它们在最终结果中只出现了一次,因为 UNION 运算符排除了查询结果中的重复记录。

SQL 并集运算的语法如下:

SELECT column1, column2, ...
FROM table1
UNION [DISTINCT | ALL]
SELECT col1, col2, ...
FROM table2;

其中,DISTINCT 表示对合并的结果集进行去重操作,只保留不重复的记录。ALL 表示保留最终结果中的重复记录。如果省略,默认值为 DISTINCT。

以下是一个 UNION 运算符的示例:

SELECT id, name
FROM t_set1 
UNION
SELECT id, name
FROM t_set2;

查询返回的结果如下:

id|name 
--|------
 1|apple 
 2|banana
 3|orange
 4|pear 

虽然“apple”和“banana”在两个表中都存在,但是它们在最终的结果中只出现了一次。

UNION 运算符可以改写为等价的全外连接查询。例如,上面的查询语句可以改写为下面这样:

-- Oracle、Microsoft SQL Server、PostgreSQL 以及 SQLite 
SELECT COALESCE(t1.id, t2.id), COALESCE(t1.name, t2.name)
FROM t_set1 t1
FULL JOIN t_set2 t2
ON (t2.id = t1.id AND t2.name = t1.name);

其中,全外连接可以返回左表和右表中的全部数据,COALESCE 函数的作用就是当左表字段为空时返回右表中的字段。MySQL 目前不支持全外连接查询。

如果我们想要保留并集运算结果中的重复记录,可以使用 UNION ALL 运算符。例如:

SELECT id, name
FROM t_set1 
UNION ALL
SELECT id, name
FROM t_set2;

查询返回的结果如下:

id|name 
--|------
 1|apple 
 2|banana
 3|orange
 1|apple 
 2|banana
 4|pear 

此时,“apple”和“banana”在结果中分别出现了两次。

提示:通常来说,UNION ALL 运算符无须进行重复值的去除,其性能比 UNION 运算符更好(尤其在数据量比较大的情况下)。

对于 UNION 和 UNION ALL 运算符,两个查询结果必须包含相同数量的字段,同时对应字段的数据类型也要兼容。不过,MySQL 和 SQLite 会执行隐式的数据类型转换,例如:

-- MySQL 和 SQLite
SELECT 1 AS id
UNION ALL
SELECT 'sql' AS name;

MySQL 将第一个查询返回的字段转换为字符串类型,SQLite 将第二个查询返回的字段转换为整数类型。查询返回的结果如下:

id 
---
1 
sql

差集排他

SQL 差集运算的运算符是 EXCEPT,它可以用于计算两个查询结果集的相减,返回出现在第一个查询结果集中但不在第二个查询结果集中的数据,如下图所示。

在这里插入图片描述

图 中第一个查询的结果只有元素 3 没有出现在第二个查询的结果中,因此差集运算的结果只返回了 3。SQL 差集运算的语法如下:

SELECT column1, column2, ...
FROM table1
EXCEPT [DISTINCT | ALL]
SELECT col1, col2, ...
FROM table2;

其中,DISTINCT 表示对合并的结果集进行去重操作,只保留不重复的记录。ALL 表示保留最终结果集中的重复记录。如果省略,默认值为 DISTINCT。

注意:MySQL 8.0 开始支持 EXCEPT 运算符以及 ALL 选项。Oracle 21c 开始支持 EXCEPT 关键字以及 ALL 选项,其以前的版本使用等价的 MINUS 运算符。PostgreSQL 支持完整的 DISTINCT 和 ALL 选项,SQL Server 以及 SQLite 支持简写的 EXCEPT。

以下是一个 EXCEPT 运算符的示例:

SELECT id, name
FROM t_set1 
EXCEPT
SELECT id, name
FROM t_set2;

如果使用 Oracle 19c 以及更早的版本,等价的查询语句如下:

-- Oracle
SELECT id, name
FROM t_set1 
MINUS
SELECT id, name
FROM t_set2;

查询返回的结果如下:

id|name 
--|------
 3|orange

只有“orange”出现在表 t_set1 但不在表 t_set2 中。

差集运算可以改写为等价的左外连接或者右外连接,上面的查询语句可以改写为下面这样:

SELECT t1.id, t1.name
FROM t_set1 t1
LEFT JOIN t_set2 t2
ON (t2.id = t1.id AND t2.name = t1.name)
WHERE t2.id IS NULL;

其中的 WHERE 条件是关键,它保留了连接结果中 t_set2.id 为空的数据,也就是只在 t_set1 中出现的记录。

集合运算与排序

我们在使用集合运算符时需要注意几个事项,首先就是排序操作。如果我们想要对集合运算的结果进行排序操作,必须将 ORDER BY 子句写在整个查询语句的最后,集合运算符之前的 SELECT 语句中不能出现排序子句。

下面是一个错误的查询示例:

-- 集合运算中的错误排序子句
SELECT id, name
FROM t_set1
ORDER BY id
UNION ALL
SELECT id, name
FROM t_set2;

无论我们使用哪种数据库,以上语句都会返回语法错误。因为在集合运算之前进行排序没有意义,最终结果的返回顺序可能会发生改变。正确的做法是在整个查询语句的最后指定排序操作,例如:

SELECT id, name
FROM t_set1
UNION ALL
SELECT id, name
FROM t_set2
ORDER BY id;

查询返回的结果如下:

id|name 
--|------
 1|apple 
 1|apple 
 2|banana
 2|banana
 3|orange
 4|pear 

运算符的优先级

另一个关于集合运算的注意事项就是 3 种集合运算符的优先级。当我们使用集合运算符将多个查询语句进行组合时,需要注意它们之间的优先级和执行顺序:

  • 按照 SQL 标准,交集运算符(INTERSECT)的优先级高于并集运算符(UNION)和差集运算符(EXCEPT)。但是 Oracle 和 SQLite 中所有集合运算符的优先级相同。
  • 相同的集合运算符按照从左至右的顺序执行。
  • 使用括号调整多个集合运算符的执行顺序。

以下示例说明了不同集合运算符的执行优先级:

-- Microsoft SQL Server、PostgreSQL 以及 SQLite
SELECT 1 AS n
UNION ALL
SELECT 1
INTERSECT 
SELECT 1;

以上语句在 Microsoft SQL Server 和 PostgreSQL 中返回的结果如下:

n
-
1
1

查询返回了 2 个重复的 1。因为查询先执行 INTERSECT 运算符,结果包含 1 个 1。然后执行 UNION ALL 运算符,最终的结果保留了重复的 1。

以上语句在 Oracle 和 SQLite 中返回的结果如下:

n
-
1

查询只返回了 1 个 1。因为查询先执行 UNION ALL 运算符,结果包含 2 个 1。然后再执行 INTERSECT 运算符,最终的结果去除了重复值。

以下示例说明了相同集合运算符的执行顺序:

SELECT 1 AS n
UNION ALL
SELECT 1
UNION
SELECT 1;

查询返回的结果如下:

n
-
1

以上语句只返回了 1 个 1,因为第二个 UNION 运算符去除了重复的记录。

如果我们将以上示例中的两个并集运算符交换位置:

SELECT 1 AS n
UNION
SELECT 1
UNION ALL
SELECT 1;

查询返回的结果如下:

n
-
1
1

以上语句返回了 2 个重复的 1,因为第二个 UNION ALL 运算符保留了重复的记录。

最后,我们可以在使用括号来修改多个集合运算符的执行顺序:

-- MySQL、Oracle、Microsoft SQL Server 和 PostgreSQL
SELECT 1 AS n
UNION ALL
(SELECT 1
INTERSECT
SELECT 1);

以上示例先执行括号内的查询语句,因此查询返回的结果如下:

n
-
1
1

SQLite 目前不支持这种修改集合运算符优先级的方式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/557777.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

neo4j使用详解(十八、java driver使用及性能优化<高级用法>——最全参考)

Neo4j系列导航&#xff1a; neo4j安装及简单实践 cypher语法基础 cypher插入语法 cypher插入语法 cypher查询语法 cypher通用语法 cypher函数语法 neo4j索引及调优 neo4j java Driver等更多 1.依赖引入 <dependency><groupId>org.neo4j.driver</groupId><…

线程池 ThreadPoolExecutor 配置参数详解

《开发语言-Java》 线程池 ThreadPoolExecutor 参数详解 一、引言二、主要内容2.1 核心构造函数2.2 核心线程数2.3 最大线程数2.4 空闲线程存活时间2.5 keepAliveTime 的时间单位2.6 核心线程在空闲时的回收策略2.7 工作队列2.8 线程工厂2.9 拒绝策略 三、总结 一、引言 提到 …

密码学 | 承诺:基本概念

目录 正文 1 承诺的交互 2 承诺的属性 3 硬币抛掷问题 3.1 朴素版方案 3.2 承诺版方案 &#x1f951;源自&#xff1a;https://en.wikipedia.org/wiki/Commitment_scheme &#x1f951;写在前面&#xff1a;英文的承诺是 commitment scheme&#xff0c;否则很难进行…

项目实践---贪吃蛇游戏(游戏的概述)

这里要准备三个文件&#xff1a;1.头文件&#xff08;snake.h&#xff09; 2.测试文件&#xff08;test.c&#xff09; 3.主文件&#xff08;snake.c&#xff09; 贪吃蛇游戏是一个经典的C语言代码实现的项目&#xff0c;大约500行代码。对于大家来说&#xff0c;贪吃蛇都玩过…

如何用个人电脑搭建一台本地服务器,并部署项目到服务器详细教程

服务器是一种高性能计算机&#xff0c;作为网络的节点&#xff0c;它存储、处理网络上80%的数据、信息&#xff0c;因此也被称为网络的灵魂。与普通计算机相比&#xff0c;服务器具有高速CPU运算能力、长时间可靠运行、强大I/O外部数据吞吐能力以及更好的扩展性。 服务器的主要…

【QT进阶】Qt Web混合编程之html、 js的简单交互

往期回顾 【QT进阶】Qt Web混合编程之VS2019 CEF的编译与使用&#xff08;图文并茂超详细介绍&#xff09;-CSDN博客【QT进阶】Qt Web混合编程之QWebEngineView基本用法-CSDN博客【QT进阶】Qt Web混合编程之CMake VS2019编译并使用QCefView&#xff08;图文并茂超详细版本&…

NSSCTF Round#22 Reverse个人专项赛 WP

1. ezcrypt&#xff08;史&#xff09; pyinstxtractor.py解包exe&#xff0c;然后pycdc反编译NSSCTF.pyc 得到的源码并不完整&#xff0c;但是重要的部分已经有了&#xff0c;就是一个blowfish加密 但是密钥是crypto.SomeEncode&#xff0c;这并不是字面意义的字符串&#x…

基于弹簧鞘复合纱和迁移学习算法的可穿戴人体重构和智能试衣系统

研究背景 在信息时代和元宇宙的背景下&#xff0c;虚拟服装设计对满足服装行业的个性化需求至关重要。与传统方法不同&#xff0c;虚拟试衣节省时间、方便客户&#xff0c;并提供多样化的款式。准确得测量人体围度并重构出人体的模型是虚拟试衣的关键。为了实现动态人体重构&a…

路径规划 | RRT结合APF算法快速探索随机树结合人工势场法的路径规划算法(Matlab)

目录 效果一览基本介绍程序设计参考文献 效果一览 基本介绍 RRT结合APF算法的matlab代码。地图为可以替换的栅格地图。代码是在复现华中科技大学发表的英文论文的基础上的进一步改进。RRT算法。人工势场算法。 1.原论文方法简介&#xff1a;针对快速探索随机树&#xff08;RRT&…

用 Pytorch 训练一个 Transformer模型

昨天说了一下Transformer架构&#xff0c;今天我们来看看怎么 Pytorch 训练一个Transormer模型&#xff0c;真实训练一个模型是个庞大工程&#xff0c;准备数据、准备硬件等等&#xff0c;我只是做一个简单的实现。因为只是做实验&#xff0c;本地用 CPU 也可以运行。 本文包含…

C++ STL 容器 vector

目录 1. vector 对象2. vector 大小 size 和 容量 capacity3. vector 成员函数3.1 迭代器3.2 容量3.3 元素访问3.4 插入3.5 删除3.6 动态扩充与收缩 4. vector 迭代器失效问题总结其他补充 本文测试环境为 编译器 gcc 13.1 vector 是 STL 中的一个顺序容器&#xff0c;它给我们…

如何将静态网页资源“打包“成.exe或者.apk

Hello , 我是小恒不会java。最近有音乐播放器win桌面应用程序的需求&#xff0c;那就说说上手electron 又想到很多人对apk文件不太了解&#xff0c;apk文件就是安卓桌面应用程序&#xff0c;比如你手机现在打开的微信 当然&#xff0c;exe文件基本都清楚&#xff0c;windows可执…

正则表达式(Regular Expression)

正则表达式很重要&#xff0c;是一个合格攻城狮的必备利器&#xff0c;必须要学会&#xff01;&#xff01;&#xff01; &#xff08;参考视频&#xff09;10分钟快速掌握正则表达式&#xff08;奇乐编程学院&#xff09;https://www.bilibili.com/video/BV1da4y1p7iZ在线测试…

React Hooks(常用)笔记

一、useState&#xff08;保存组件状态&#xff09; 1、基本使用 import { useState } from react;function Example() {const [initialState, setInitialState] useState(default); } useState(保存组件状态) &#xff1a;React hooks是function组件(无状态组件) &#xf…

再拓信创版图-Smartbi 与东方国信数据库完成兼容适配认证

近日&#xff0c;思迈特商业智能与数据分析软件 [简称&#xff1a;Smartbi Insight] V11与北京东方国信科技股份有限公司 &#xff08;以下简称东方国信&#xff09;CirroData-OLAP分布式数据库V2.14.1完成兼容性测试。经双方严格测试&#xff0c;两款产品能够达到通用兼容性要…

浪潮信息成功打造大规模、高性能、高可靠的单存储集群方案!

为帮助企业应对商业智能应用中面临的关于海量数据存储及实时分析的难题&#xff0c;浪潮信息日前通过技术研发&#xff0c;创新推出全球首个SAP HANA集群方案&#xff0c;该方案实现了最大可支持HANA集群服务器节点数量的翻倍&#xff0c;单存储即可支持16节点的&#xff0c;大…

图片高效批量管理,一键批量旋转150度,高效整理您的图片库

在数字化时代&#xff0c;我们的生活中充满了各种图片。从手机拍照到网络下载&#xff0c;从社交媒体到工作文档&#xff0c;图片无处不在。然而&#xff0c;随着图片数量的不断增加&#xff0c;如何高效管理这些图片&#xff0c;让它们有序、易于查找&#xff0c;成为了许多人…

Vue3从入门到实战:深度了解相关API

shallowRef 作用&#xff1a;创建一个响应式数据&#xff0c;但只对顶层属性进行响应式处理。 用法&#xff1a; let myVar shallowRef(initialValue); 特点&#xff1a;只跟踪引用值的变化&#xff0c;不关心值内部的属性变化。 shallowReactive 作用&#xff1a;创建一个…

【MySQL】表的基本约束

文章目录 1、约束类型1.1NOT NULL约束1.2UNIQUE&#xff1a;唯一约束1.3DEFAULT&#xff1a;默认值约束1.4PRIMARY KEY&#xff1a;主键约束1.5FOREIGN KEY&#xff1a;外键约束 2、表的设计2.1一对一2.2一对多2.3多对多 1、约束类型 关键字解释NOT NULL指示某列不能存储NULL值…

点赞列表查询列表

点赞列表查询列表 BlogController GetMapping("/likes/{id}") public Result queryBlogLikes(PathVariable("id") Long id) {return blogService.queryBlogLikes(id); }BlogService Override public Result queryBlogLikes(Long id) {String key BLOG_…