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

news2024/12/23 1:47:56

关系型数据库中的表与集合理论中的集合类似,表是由行(记录)组成的集合。因此,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 目前不支持这种修改集合运算符优先级的方式。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

本地项目如何设置https——2024-04-19

问题:由于项目引用了html5-qrcode插件,但是该插件在本地移动端调试时只能使用https访问,所有原本的本地地址是http,就需要改成https以方便调试。 解决方法:使用本地https证书 1)从项目文件下打开cmd逐步输…

AOP基础

一、AOP概述 AOP:Aspect Oriented Programming(面向切面编程、面向方面编程),其实就是面向特定方法编程。 使用场景:①记录操作日志;②权限控制;③事务管理等。 优势:①代码无侵入…

SRS服务接入华为云CDN

一、srs配置 正常的标准配置即可,需打开hls推流即可,一般配置中默认打开 二、华为云cdn配置 1.登录华为云,找内容分发网络cdn 2.点击域名管理,点击添加域名 3.配置加速域名 4.选择点播加速 5.添加源站 配置源站地址&#xf…

2024年【G1工业锅炉司炉】考试及G1工业锅炉司炉考试内容

题库来源:安全生产模拟考试一点通公众号小程序 2024年【G1工业锅炉司炉】考试及G1工业锅炉司炉考试内容,包含G1工业锅炉司炉考试答案和解析及G1工业锅炉司炉考试内容练习。安全生产模拟考试一点通结合国家G1工业锅炉司炉考试最新大纲及G1工业锅炉司炉考…

删除顺序表中所有值为X的元素(顺序表,单链表)

目录 时间复杂度为O(1)(顺序表):代码实现: 运行结果: 时间复杂度为O(n)(顺序表):代码实现: 运行结果: 单链表:时间复杂度o(n):代码实现: 时间复杂度为O(1…

20240415,构造函数和析构函数,拷贝构造函数调用时机规则

目录 二,对象的初始化和清理 2.1 构造函数和析构函数 2.2 函数分类及调用 2.3 拷贝构造函数调用时机 2.4 构造函数调用规则 二,对象的初始化和清理 2.1 构造函数和析构函数 解决初始化和清理问题,编译器自动调用,如果不提…

探索异常传播:深入剖析Python中的错误处理机制

文章目录 1. 异常传播的基本原理2. 复杂的异常传播场景3. 再次抛出异常的意义是什么?4. 最佳实践与异常处理策略 理解异常传播(也称为异常冒泡)的过程是至关重要的。这一机制确保当在程序执行中发生错误时,错误能被有效地捕获和处…

CentOS7下安装mysql8或者mysql5.7

mysql8 1、下载 访问mysql官网下载mysql8软件包 https://dev.mysql.com/downloads/mysql/ 选择相应的版本如:RPM Bundle mysql-8.0.33-1.el7.x86_64.rpm-bundle.tar RPM Bundle 8.0.33 下载地址:https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.…

springboot结合vue实现文件上传下载功能

紧接着上一次的博客,这次来实现一下文件(主要是图片)的上传和下载功能,上一次的博客如下所示: Springboot集成JWT token实现权限验证-CSDN博客 其实文件的上传和下载功能(后端的部分),在我之前的博客就已经有写了,所以…

【学习】关于测试技术的重要性和挑战有哪些

随着信息技术的飞速发展,软件产品已成为现代社会不可或缺的一部分。在软件研发过程中,测试技术的重要性日益凸显。它不仅是确保软件质量的关键环节,也是降低软件故障风险、提高用户体验的重要手段。然而,测试技术也面临着诸多挑战…

【触想智能】如何选购到一款合适的工业电脑一体机

工业电脑一体机是专为工业环境而设计的一种工业计算机。工业电脑一体机和普通的计算机不一样,它对产品的参数性能要求很高,因为它们通常会运行在高低温、电磁干扰、高粉尘、湿度大的恶劣环境中,所以相应的要求工业电脑一体机必须具备良好的宽…

Qt日志使用

QsLog使用 这篇讲qt的日志还是比较好的,可以在自己的函数里面配置这个日志框架实现自己所需的功能。 我接触的项目里面,假如有个函数功能执行错误了,我希望可以快速定位到这个错误,这个时候就需要到了日志,我咨询了有经…

LeetCode 113—— 路径总和 II

阅读目录 1. 题目2. 解题思路3. 代码实现 1. 题目 2. 解题思路 看到树的问题一般我们先考虑一下是否能用递归来做。 假设 root 节点的值为 value,如果根节点的左子树有一个路径总和等于 targetSum - value,那么只需要将根节点的值插入到这个路径列表中…

VUE 页码分页封装

VUE 页码封装组件 pagination/index.vue &#xff1a; <template><div class"pagination-contianer"><el-pagination background layout"prev, pager, next" :total"total" current-change"currentChange"> </e…

jdk17 +MAT进行内存分析

MemoryAnalyzer要进行内存分析&#xff0c;需要Dump快照 文件 手动获取Dump快照 文件 jmap -dump:live,formatb,file/path/to/heapdump.hprof <PID>然后再MAT 进行分析&#xff1a;

基于达梦数据库开发-C#篇

文章目录 前言一、相关准备二、主要代码1.引入达梦类库2.连接达梦数据库3.DmCommand方式获取达梦数据库信息4.DmDataAdapter方式获取达梦数据库信息 总结 前言 达梦数据库是国产的新一代大型通用关系型数据库&#xff0c;全面支持 SQL 标准和主流编程语言接口/开发框架。其中.…

LD-Pruner、EdgeFusion(On-Device T2I)、FreeDiff、TextCenGen、MemLLM

本文首发于公众号&#xff1a;机器感知 https://mp.weixin.qq.com/s/KiyNfwYWU-wBiCO-hE9qkA 苏 The devil is in the object boundary: towards annotation-free instance segmentation using Foundation Models Foundation models, pre-trained on a large amount of data…

arm64-v8a、armeabi-v7a、x86、x86_64

当我们去GitHub下载应用的时候是不是经常很懵逼&#xff0c;就像下图一样&#xff0c;粗看一下如此多安装包到底要选择下载哪个且每种安装包到底有哪差别&#xff1f;毕竟因为自己一无所知&#xff0c;有时便随意下载一个后&#xff0c;安装时却报『此版本与你的系统不兼容』的…

TCP三次握手,但通俗理解

如何用通俗的语言来解释TCP&#xff08;传输控制协议&#xff09;的三次握手过程&#xff1f; 想象一下你正在和朋友电话沟通&#xff0c;但你们之间不是心灵感应&#xff0c;而是需要通过清晰地听到对方的声音来确认通话质量良好。TCP三次握手就像是在电话拨通之前&#xff0…

JavaScript 流程控制-循环

一、循环 二、 for 循环 重复执行的语句被称为循环体&#xff0c;能否继续重复执行&#xff0c;取决于循环的终止条件。 由循环体及循环的终止条件组成的语句被称为循环语句 1、语法结构 for 循环 主要用于把某些代码循环若干次&#xff0c;通常跟计数有关 for &#xff08…