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 ...
随机推荐
- 使用selenium 检测js报错
背景:接到一个需求,想检测页面是否能检测js报错,何为js报错,如下图所示,在控制台中,使用console,如果有js报错,就会出现错误 如何检测,简单版操作,打开一个url,使用manage获取浏览 ...
- python 3+djanjo 2.0.7简单学习(一)
1.安装django pip install django 我这里已经安装过了 整个目录结构如下: votes : migrations : __init__.py : admin.py : apps ...
- Python实现接口测试中的常见四种Post请求数据
前情: 在日常的接口测试工作中,模拟接口请求通常有两种方法, 利用工具来模拟,比如fiddler,postman,poster,soapUI等 利用代码来模拟,使用到一些网络模块,比如HttpClie ...
- android ndk中使用gprof
1.下载gprof支持库 下载地址: http://code.google.com/p/android-ndk-profiler/ 2.代码上的修改添加 在初始化时: monstartup(" ...
- 经典sql语句汇总
1,某条数据放首位,其他倒序并分页 select * from Student order by( case when id='2' then 1 ELSE 4 END),id desc l ...
- spring-bean(注解方式-管理及依赖注入)
Bean管理(注解方式) 1.添加注解的依赖包:Spring-aop.jar 2.配置spring的XML文件的引入(查官方源码) 3.开启注解的扫描 <context:component-sc ...
- Servlet学习笔记01——什么是servlet?
1.什么是Servlet? sun公司制订的一种用来扩展web服务器功能的组件规范. (1)扩展web服务器功能 早期的web服务器(apache web server,iis) 只能处理静态资源的请 ...
- linux常见内核参数
参数 描述 net.ipv4.ip_forward 接口间转发报文net.ipv4.tcp_tw_reuse 表示是否允许将处于 TIME-WAIT 状态的 socket (TIME-WAIT 的端口 ...
- Manacher算法:求解最长回文字符串,时间复杂度为O(N)
原文转载自:http://blog.csdn.net/yzl_rex/article/details/7908259 回文串定义:"回文串"是一个正读和反读都一样的字符串,比如&q ...
- LInux操作随手笔记
一.find 的用法 实例 find / -name test.txt 就可以找到这个文件的路径(如果存在). 二.学用vi编辑器,学用rz往linux服务器上面上传文件 linux中rz 和 sz ...