SQL 与关系代数
Table of Contents
前言
SQL 是基于关系代数的查询语言,假如学习过 SQL 和关系代数,你就会发现,在 SQL 的查询语句中你会发现很多关系代数的影子。
然而,虽然知道 SQL 和关系代数密切相关,但是我也不知道学了关系代数对学习 SQL 有什么好处,因此,如果你对关系代数没兴趣的话,现在就可以关掉这篇博客了。
关系与表
我们可以把数据库中的表和关系代数中的关系看做是一个东西,这里给出接下来会用到的两个关系(表):
User
+------+---------+--------+
| id | account | passwd |
+------+---------+--------+
| 1 | 123 | ****** |
| 2 | 456 | ****** |
+------+---------+--------+
Profile
+------+------+------+
| id | name | age |
+------+------+------+
| 1 | tony | 16 |
| 3 | john | 2 |
+------+------+------+
关系代数的基本运算
关系代数的基本运算包括:选择、投影、并、集合差、笛卡尔积和更名。
投影
这里我们可以先来看一看 投影 运算,它的作用和 SQL 中的 SELECT 基本相同。
比如说我们要选择 User 中的 account, 用 SQL 编写的话就是:
SELECT account FROM user;
如果用关系代数来写的话,就可以写成 \(\prod_{account}(user)\).
选择多列就可以这样: \(\prod_{id,account}(user)\).
选择
由于一些历史原因,关系代数中的选择和 SQL 中的 SELECT 不是一个意思,而是更接近 WHERE, 我们可以通过选择运算选择关系中符合指定条件的部分。
比如说 \(\sigma_{id=1}(user)\) 可以选择关系 User 中 id 等于 1 的用户,其等价的 SQL 语句如下:
SELECT * FROM user WHERE id = 1;
选择运算中可以使用的谓词包括: \(=, \neq, <, \leqslant, >, \geqslant\). 同时还可以使用连词 \(and(\land), or(\lor), not(\lnot)\) 将多个谓词合并为一个较大的连词。
比如说 \(\sigma_{id \geqslant 1 \land id < 3}\) 选择 id 范围在 [1, 3) 之间的用户,等价于:
SELECT * FROM user WHERE id >= 1 AND id < 3;
同时,由于关系运算的结果依然是一个关系,因此,我们可以将关系运算组合起来,比如:选择 id 为一的用户的 account 可以表示为 \(\prod_{account}(\sigma_{id=1}(user))\)
并运算
并运算可以将两个集合并起来,对应到 SQL 中就是 UNION 操作,比如说获取 User 和 Profile 中的所有 ID:
SELECT id FROM user UNION SELECT id FROM profile;
用关系代数来表示的话就是: \(\prod_{id}(user) \cup \prod_{id}(profile)\).
关系代数的并运算和 SQL 中的 UNION 一样,要求需要并起来的关系的 列 是相同的,同时,比 SQL 更严格的是,关系代数的并运算还要求列的位置相同。
集合差运算
集合差运算可以从一个集合中排除另一个集合中的内容,对于到 SQL 中就是 EXCEPT 操作,比如获取 User 不在 Profile 中的所有 ID1:
SELECT id FROM user EXCEPT SELECT id FROM profile;
用关系代数来表示的话就是: \(\prod_{id}(user) - \prod_{id}(profile)\).
集合差运算对不同关系的要求和并运算是相同的。
笛卡尔积
笛卡尔积是一个很重要的运算,通过笛卡尔积我们可以将 任意 两个关系的信息结合在一起,笛卡尔积的运算结果会将两个关系的所有列作为新的关系的列,将两个关系的所有行的组合作为新的关系的行。
对应到 SQL 中便是 CROSS JOIN, 比如说如下 SQL 语句便可以表示为 \(user \times profile\):
SELECT * FROM user CROSS JOIN profile;
运算结果如下:
+------+---------+--------+------+------+------+
| id | account | passwd | id | name | age |
+------+---------+--------+------+------+------+
| 1 | 123 | ****** | 1 | tony | 16 |
| 2 | 456 | ****** | 1 | tony | 16 |
| 1 | 123 | ****** | 3 | john | 2 |
| 2 | 456 | ****** | 3 | john | 2 |
+------+---------+--------+------+------+------+
更名运算
关系代数中的更名运算对应到 SQL 中便等价于 AS 操作,可以对关系进行更名也可以对列进行更名操作:
- 更名关系 - \(\rho_{users}(user)\)
- 更名列 - \(\rho_{users(uid,account,password)}(user)\)
在进行连接操作的时候常常会用到更名操作,而 SQL 中的更名操作用起来比关系代数中的方便一些,形象一些。
关系代数的附加运算
关系代数的附加运算是可以通过基本运算推导得出的,包括集合交运算和各类连接运算。
集合交运算
集合交运算计算两个关系中都存在的部分,可以通过基本运算表示: \(r \cap s = r - (r - s)\).
集合交运算对于的 SQL 语句是 INTERSECT, 比如:
SELECT id FROM user INTERSECT SELECT id FROM profile;
表示为关系代数便是 \(\prod_{id}(user) \cap \prod_{id}(profile)\).
连接运算
个人认为连接运算是所有运算中最难的一种,它存在很多分类,比如:自然连接、内连接、外连接等。
同时,不同的连接运算之间还存在不浅的关系,因此,需要好好理解才行。
自然连接
首先是自然连接,自然连接将两个关系的 属性集 的 并集 作为新的关系的属性,同时会对两个关系中的相同属性进行比较筛选。
假如两个关系不存在相同的属性,那么自然连接的结果便和 笛卡尔积 相同:
+------+---------+--------+------+------+
| id | account | passwd | name | age |
+------+---------+--------+------+------+
| 1 | 123 | ****** | tony | 16 |
+------+---------+--------+------+------+
如上便是 自然连接 的运算结果,它将关系 User 和 Profile 的属性的并集作为新关系的属性,同时筛选具有相同 ID 值的行。
连接运算的关系代数形式都很复杂,这里就简单列出对应的 SQL 语句好了2:
SELECT * FROM user NATURAL JOIN profile;
内连接
可以把内连接3 看做添加了选择语句的笛卡尔积,也就是说,计算内连接时需要先行计算出笛卡尔积,然后在根据选择条件进行选择。
比如这样的内连接操作:
SELECT * FROM user INNER JOIN profile ON user.id >= profile.id;
其结果为:
+------+---------+--------+------+------+------+
| id | account | passwd | id | name | age |
+------+---------+--------+------+------+------+
| 1 | 123 | ****** | 1 | tony | 16 |
| 2 | 456 | ****** | 1 | tony | 16 |
+------+---------+--------+------+------+------+
这里可以对照笛卡尔积的计算结果进行理解:
+------+---------+--------+------+------+------+
| id | account | passwd | id | name | age |
+------+---------+--------+------+------+------+
| 1 | 123 | ****** | 1 | tony | 16 |
| 2 | 456 | ****** | 1 | tony | 16 |
| 1 | 123 | ****** | 3 | john | 2 |
| 2 | 456 | ****** | 3 | john | 2 |
+------+---------+--------+------+------+------+
外连接
我们可以把外连接看做是 内连接 的扩展4,首先计算出两个关系内连接的结果,然后根据外连接的类型补充数据到内连接的结果上。
比如说左外连接,首先可以计算出 User 和 Profile 的内连接,然后用空值来填充在左侧关系中存在而右侧关系中不存在的项就可以了。
SELECT * FROM user LEFT JOIN profile on user.id = profile.id;
这条 SQL 语句的执行结果为:
+------+---------+--------+------+------+------+
| id | account | passwd | id | name | age |
+------+---------+--------+------+------+------+
| 1 | 123 | ****** | 1 | tony | 16 |
| 2 | 456 | ****** | NULL | NULL | NULL |
+------+---------+--------+------+------+------+
如果将其替换为内连接的话便是:
+------+---------+--------+------+------+------+
| id | account | passwd | id | name | age |
+------+---------+--------+------+------+------+
| 1 | 123 | ****** | 1 | tony | 16 |
+------+---------+--------+------+------+------+
可以看到,ID 为 2 的项只存在于 User 中而不存在与 Profile 中,因此,左外连接时使用了空值来填充 Profile 对应的部分,保证 User 的每项都存在。
依次类推,右外连接、全外连接也就好理解了:
右外连接的执行结果:
+------+---------+--------+------+------+------+
| id | account | passwd | id | name | age |
+------+---------+--------+------+------+------+
| 1 | 123 | ****** | 1 | tony | 16 |
| NULL | NULL | NULL | 3 | john | 2 |
+------+---------+--------+------+------+------+
全外连接的执行结果:
+------+---------+--------+------+------+------+
| id | account | passwd | id | name | age |
+------+---------+--------+------+------+------+
| 1 | 123 | ****** | 1 | tony | 16 |
| 2 | 456 | ****** | NULL | NULL | NULL |
| NULL | NULL | NULL | 3 | john | 2 |
+------+---------+--------+------+------+------+
其实,这三个外连接是可以互相转换的,将两个关系的位置换一下就可以将左外连接转换为右外连接,而将左右外连接的结果并起来就可以得到全外连接了。
结语
虽然说关系代数和 SQL 有不浅的关系,但是学了关系代数,对编写 SQL 也没有多大的帮助 @_@
而且,不同的数据库实现 SQL 的语法还存在细微的差别……
也许,可以借助关系代数表达式来生成 SQL 语句!
其实关系代数还有一些扩展运算,对应到 SQL 中便是聚集、分组之类的,博客中没有说到,有兴趣的话可以去了解一下。
或者什么时候有时间了补上 @_@
Footnotes
1 不同数据库对 EXCEPT 子句的支持存在区别,这里的 SQL 语句不一定能运行通过
3 其实在关系代数中内连接应该叫做 theta 连接, 这里主要是为了和 SQL 相对应
4 其实按照书《数据库系统概念》中的描述的话应该是 自然连接, 但是实际的操作结果更符合 内连接, 虽然说,内连接也可以看做是自然连接
SQL 与关系代数的更多相关文章
- sql是最成功的第四代语言
SQL发展的前世今生 很多年前,两名年轻的IBM研究员将一门关系型语言带到了数据库领域,旨在使用声明性的方式来操作数据.从Don Chamberlin和Ramond Boyce发表"SEQU ...
- 【翻译】Flink Table Api & SQL —Streaming 概念 ——动态表
本文翻译自官网:Flink Table Api & SQL 动态表 https://ci.apache.org/projects/flink/flink-docs-release-1.9/de ...
- DDD:建模原语 之 四象图(转载的神文)
“模型.状态和行为特征.场景”和“四象图”,建模观的命名与立象. 建模原语:四象图 作者:achieveidea@gmail.com 命名:模型.结构特征.行为特征.场景(及其规约). 释义:模型,描 ...
- 数据模型与查询语言 ------《Designing Data-Intensive Applications》读书笔记2
数据模型是开发软件的最重要的部分,因为它们对应用程序有着深远的影响:不仅是软件的编写方式,而且也影响我们如何解决的问题的方式.第二篇读书笔记,我们聊一聊数据模型的设计. 1.数据模型的分层 作为一个开 ...
- 【转载】【Todo】银弹与我们的职业
看到一段文字,不得不单独拎出来. 然后再借用一下g9老大的<银弹和我们的职业>中的话: 银弹和我们的职业发展有什么相干?很简单:我们得把时间用于学习解决本质困难.新技术给高手带来方便.菜鸟 ...
- 春蔚专访--MaxCompute 与 Calcite 的技术和故事
摘要:2019大数据技术公开课第一季<技术人生专访>,来自阿里云计算平台事业部高级开发工程师雷春蔚向大家讲述了MaxCompute 与 Calcite 的技术和故事. 具体内容包括: 1) ...
- 第10讲:利用SQL语言实现关系代数操作
一.实现并.交.差运算 1. 基本语法形式:子查询 [union [all] | intersect [all] | except [all] 子查询] ①意义:将关系代数中的∪.∩.- 分别用uni ...
- 数据库系统概论学习3-SQL 语句和关系代数(一)SQL 入门
3. SQL 语句和关系代数(一)SQL 入门 3.1 数据库的编程语言 SQL 的优点 SQL 集成了数据查询(data query).数据操作(data manipulation).数据定义(da ...
- 关系代数(Relation Algebra)与SQL语句的对应关系
SQL语句的执行一般是先翻译为关系代数再被执行的(能有效提高执行速度),所以我们有必要 了解关系代数与SQL语句间的对应关系. 就像高中代数由+-*/和数字组成,关系代数是由union.interse ...
随机推荐
- TCP的可靠连接是如何产生的?
http://bbs.csdn.net/topics/190011812 看过TCP/IP的源代码没?tcp中所谓的连接只是在tcp的tcb中存储了对端的地址信息,并且记录连接的状态,通过重发之类的来 ...
- 使用 NetBackup 命令创建 Hyper-V 策略(命令创建其他策略也是如此)
Veritas NetBackup™ for Hyper-V 管理指南 Product(s): NetBackup (8.1) 使用 NetBackup 命令创建 Hyper-V 策略 本主题介绍如何 ...
- 【转】Android 组件系列-----Activity保存状态
本篇随笔将详细的讲解Activity保存状态的概念,也就是saving activity state. 一.Activity状态保持概念 保存Activity的状态是非常重要的,例如我们在玩一个游戏的 ...
- php简单开启gzip压缩方法(zlib.output_compression)
网上的教程基本是你抄我来我抄他,不外乎加头加尾或者自构函数两种写法.实际上每个php页面都要去加代码——当然也可以include引用,不过总显得略微麻烦 一般而言,页面文件开启gzip压缩以后,其 ...
- 2017.9.26 request请求参数用法
4.2 访问请求参数 request对象的getParamter()方法,可以用来获取用户(客户端)提交的数据 4.2.1 访问请求参数的方法 String 自符串变量 =request.getPar ...
- B3942 Censoring
爆炸入口 有一个S串和一个T串,长度均小于1,000,000,设当前串为U串,然后从前往后枚举S串一个字符一个字符往U串里添加,若U串后缀为T,则去掉这个后缀继续流程. 这道题确乎是个很好的联系kmp ...
- data-ng-hide指令用于隐藏或显示HTML元素
<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content=&q ...
- Servlet的工作原理和生命周期
Servlet的工作原理 . Web服务器加载Servlet:Web服务器启动后,它会根据每个工程的web.xml文件去查找该工程的Servlet,并且找到这些Servlet的Class文件所在的地址 ...
- java 基础词汇 必须 第九天
Collection 集合 List 列表集合 Set 不重复集合 Linked 链表 Vector 线程安全集合 Hash 哈希值 tree 树型结构 Map 键值对集合 add 增加 remove ...
- java基础1.5版后新特性 自动装箱拆箱 Date SimpleDateFormat Calendar.getInstance()获得一个日历对象 抽象不要生成对象 get set add System.arrayCopy()用于集合等的扩容
8种基本数据类型的8种包装类 byte Byte short Short int Integer long Long float Float double Double char Character ...