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 ...
随机推荐
- JS判断手机横竖屏
在移动端开发时,有时候需要判断手机的横竖屏,那么就需要用到window.orientation这个参数,通过这个参数可以判断出手机是处在横屏还是竖屏状态. 屏幕方向对应的window.orientat ...
- 【转】Druid连接池一个设置引发的血案
https://my.oschina.net/haogrgr/blog/224010 今天在一台配置很低的机器上运行批量更新的程序~~~ 大概跑了三十分钟~~~这配置~~~这程序~~~ 然后华丽丽的报 ...
- Object类的常用方法
Object类是Java中所有类的始祖.如果没有明确的指定继承,则默认继承Object类.在Java中除了基本类型外都是Object类型的对象,包括数组. 1)equals方法 Object: pub ...
- process launch failed: Security
Xcode7/iOS9,真机测试的时候遇到这样的提示 通用-> 描述文件 -> 信任应用
- 1、SpringBoot+Mybatis整合------简单CRUD的实现
编译工具:STS 代码下载链接:https://github.com/theIndoorTrain/SpringBoot_Mybatis01/commit/b757cd9bfa4e2de551b2e9 ...
- C# CheckBoxList 实现全选/反选功能怎么写?
首先我们用RadioButtonList控件,且必须包含OnSelectedIndexChanged事件和AutoPostBack=‘true’属性, <asp:LinkButton ID=&q ...
- tcp文件下载客户端+服务端
客户端: import socket if __name__ == '__main__': # 创建tcp客户端socket tcp_client_socket = socket.socket(soc ...
- cordforce Educational Codeforces Round 47 补题笔记 <未完>
题目链接 http://codeforces.com/contest/1009 A. Game Shopping 直接模拟即可,用了一个队列来存储账单 #include <iostream> ...
- (转)想从事游戏开发,1 年内能精通 C++ 吗,还需要学习什么?
本人大约从20多年前开始学习及使用C++,但仍未达到我认为「精通」的阶段,甚至对于C++11的各种新特性也未掌握.然而因为我是在读书时自学C++的,也是游戏程序员(原问题中提到题主想从事游戏开发),觉 ...
- web前端工程师面试技巧 常见问题解答
web前端工程师面试技巧 常见问题解答 每年的春招是各企业需求人才的黄金时期,不少的前端大牛或者前端新手在面试时候不知道怎么来回答面试官的问题,下面来看下我转载的这篇文章吧,希望对从事前端工作的你有所 ...