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

关系型数据库中的表与集合理论中的集合类似,表是由行(记录)组成的集合。因此,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|banana3|orange4|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|banana3|orange1|apple 2|banana4|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|banana2|banana3|orange4|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.mzph.cn/web/731.shtml

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

相关文章

chromedriver最新版下载地址

地址1.百度网盘 链接(提取码:2vo3):百度网盘 请输入提取码百度网盘为您提供文件的网络备份、同步和分享服务。空间大、速度快、安全稳固,支持教育网加速,支持手机端。注册使用百度网盘即可享受免费存储空间https://pan.baidu.com…

数据很重要,ASM磁盘组损坏,使用AMDU来抢救

欢迎关注“数据库运维之道”公众号,一起学习数据库技术! 本期将为大家分享“数据很重要,ASM磁盘组损坏,使用AMDU来抢救”的处置案例。这个案例对个人来说比较经典,下面我将把自己的处理思路进行整理与总结。 环境信息&#xff1…

安装Milvus的可视化工具Attu教程

提供两种方式来安装可视化工具Attu 一、docker安装 # 执行命令,加个 -d 在后台运行 docker run -d -p 8000:3000 -e MILVUS_URL127.0.0.1:19530 zilliz/attu:v2.2.8 至此安装完成! 浏览器输入地址 http:127.0.0.1:8000即可访问 Attu主页 如果拉取最新…

大数据操作第二天

文章目录 大数据命令的方式现在有三个命令的方式 启动一个计算圆周率的jar包方式什么是文件系统数据元数据传统的存储方式分布式存储方式元数据记录文件位置信息副本机制的方式 hafs 存储方式shell 操作大数据的方式创建目录查看文件目录下的方式上传文件的方式 大数据命令的方…

2024年国内五大企业邮箱,哪个最靠谱?

电子邮件是企业办公的重要手段,目前我国五大企业的邮箱都有Zoho Mail公司邮箱、腾讯企业邮箱、阿里企业邮箱、网易企业邮箱、263公司邮箱。可是哪个电子邮件最可靠呢?可靠的企业邮箱必须要安全性高、稳定性高,能够保护企业的隐私不被泄露&…

Git常见命令行操作和IDEA图形化界面操作

设置Git用户名和标签 在安装完Git以后需要设置用户和签名,至于为什么要设置用户签名可以看一下这篇文章【学了就忘】Git基础 — 11.配置Git用户签名说明 - 简书 (jianshu.com) 基本语法: git config --global user.name 用户名 git config --global u…

HTML学习笔记:(一)基础方法

Html格式 里面文件使用平台为&#xff1a;w3school 1、基础功能&#xff1a; <html><head> <title>这是我的第一个html页面,会显示在浏览器的标题栏中</title> </head> <!--修改背景颜色 --> <body bgcolor"yellow"> …

浅浅了解一下 LibTorch

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ LibTorch 是 PyTorch 提供的一个二进制发行版&#xff0c;包含了所有必要的头文件、库和 CMake 配置文件&#xff0c;便于开发者依赖 PyTorch 开发应用。用户可以从 PyTorch 官网下载包含最新 LibTorch…

Vitis HLS 学习笔记--scal 函数-探究

目录 1. Vitis HLS重器-Vitis_Libraries 2. 初识scal() 3. 函数具体实现 3.1 变量命名规则 3.2 t_ParEntries解释 3.3 流类型详解 3.4 双重循环 4. 总结 1. Vitis HLS重器-Vitis_Libraries 在深入探索Vitis HLS&#xff08;High-Level Synthesis&#xff09;的旅程中&…

【Leetcode】代码随想录Day16|二叉树3.0

文章目录 104 二叉树的最大深度559 n叉树的最大深度111 二叉树的最小深度222 完全二叉树的节点个数 104 二叉树的最大深度 递归法&#xff1a;无论是哪一种顺序&#xff0c;标记最大深度 class Solution(object):def depthHelper(self, root, depth):if root:depth 1left_de…

HWOD:合并整型数组

一、知识点 合并整型数组目前有两种方法 合并数组并不一定需要真正的合并 1、下意识的方法 对两个整型数组分别排序&#xff0c;然后合并 2、不排序的方法 遍历两个数组&#xff0c;找出最小值&#xff0c;输出最小值。将两个数组中与最小值相等的位置置为超大值 重复以…

VBA_MF系列技术资料1-510

MF系列VBA技术资料1-510 为了让广大学员在VBA编程中有切实可行的思路及有效的提高自己的编程技巧&#xff0c;我参考大量的资料&#xff0c;并结合自己的经验总结了这份MF系列VBA技术综合资料&#xff0c;而且开放源码&#xff08;MF04除外&#xff09;&#xff0c;其中MF01-0…

go限流、计数器固定窗口算法/计数器滑动窗口算法

go限流、计数器固定窗口算法/计数器滑动窗口算法 一、问题 问题1&#xff1a;后端接口只能支撑每10秒1w个请求&#xff0c;要怎么来保护它呢&#xff1f; 问题2&#xff1a;发短信的接口&#xff0c;不超过100次/时&#xff0c;1000次/24小时&#xff0c;要怎么实现&#xff…

豆瓣影评信息爬取 (爬虫)

代码块&#xff1a; from lxml import etree import requestsheaders{User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0 }url_list[] for i in range(0,5):i*20urlsf"https:…

链表带环问题——leetcode环形链表1 2

证明链表带环 链表的带环问题指的是本该指向NULL的最后一个节点指向了之前的节点&#xff0c;导致链表成环&#xff0c;找不到尾结点的情况&#xff0c;那么我们该如何证明链表带环呢&#xff1f; 我们可以类比物理中的追及问题&#xff0c;让快慢指针同时走&#xff0c;两者相…

企微知识库是如何搭建的?这篇文章来解答

知识库在企业中发挥着至关重要的作用&#xff0c;它不仅能够存储和整理重要的工作资料&#xff0c;还能提高员工的工作效率&#xff0c;加强团队之间的协作。对于使用微信企业版&#xff08;企业微信&#xff09;的企业来说&#xff0c;搭建一个专门的知识库是极有必要的。本文…

ceph osd分组

一、前言 使用分组可以更好的管理osd&#xff0c;将不同类型的磁盘&#xff0c;分到不同的组中&#xff0c;例如hhd类型的osd分配到hhd组&#xff0c;ssd类型的osd分配到ssd组&#xff0c;将io要求不高的分配到hhd组做存储&#xff0c;io要求高的分配到ssd组做存储 二、配置 查…

ubuntu23.10.1 php8.2安装

1、更新镜像源 apt update2、安装php apt install php php-fpm php-mysql其他扩展包&#xff0c;在后面加个-可以查看&#xff0c;选择安装,我这里是php8.2版本 apt-get install php8.2- 2.1php与nginx结合 PHP-FPM的配置文件位于/etc/php/{PHP版本}/fpm/pool.d/www.conf;…

字体反爬知识积累2

一、os模块中函数的应用 如何获取当前文件中所有文件的路径方法 这段代码使用 os.walk()函数来遍历指定目录 imgs 下的所有子目录和文件。具体来说&#xff0c;os.walk()函数返回一个生成器&#xff0c;可以在每次迭代中获取目录树中的一个元组&#xff0c;元组包含当前目录的…

【Linux C | 多线程编程】线程同步 | 条件变量(万字详解)

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; ⏰发布时间⏰&#xff1a;2024-04-15 0…