数据库
数据库可以理解为以某种方式去进行关联的数据集合,这就可以试着对现实世界的某些方面进行建模,它是大部分计算机应用程序的核心。
为什么我们需要构建数据库?
简单来说,我们可以把一份CSV文件看成我们的数据库,也就是说我们可以把数据存放在文件中,例如我们存储"艺术家、专辑":
Artist(name, year, country):
- “Wu Tang Clan”, 1992, “USA”
- “Notorious BIG”, 1992, “USA”
- “Ice Cube”, 1989, “USA”
Album(name, artist, year):
- “Enter the Wu Tang”, “Wu Tang Clan”, 1993
- “St.Ides Mix Tape”, “Wu Tang Clan”, 1994
- “AmeriKKKa’s Most Wanted”, “Ice Cube”, 1990
根据上面的数据我们其实可以写代码来进行解析,以python为例:
for line in file:
record = parse(line)
if "Ice Cube" == record[0]:
print int(record[1])
根据上面的描述不难提出下面的关键问题(文件处理系统的主要弊端):
- 我们如何确保我们的应用程序中,对于每个album(专辑)而言,要确保artist(作者)所在album中的位置都相同?
- 我们如何保证对于不同类型数据的存储是有效的?例如album的年份是四位数,若有人存了字符串?
- 我们如何存储多个作者的专辑?
- 如何查找一个固定记录?现在仅有三条数据当然块,如果有10亿的数据呢?难道每次都要打开文件进行扫描,然后再解析每行记录来查找结果吗?也就是数据访问困难。
- 我们现在是用python写的,假设是部署在web上,如果这个时候有个新需求要搞移动端,我们还能使用这个数据库吗?(需要重复造轮子吗),也就是数据孤立问题,由于数据分散在不同文件中,这些文件又可能具有不同的格式,因此编写新的应用程序来检索适当数据是很困难的
- 假设有两个线程同时对这个数据库文件进行写操作会发生什么?也就是并发访问异常
- 如何保证我们的数据是安全的?也就是安全性问题,并非所有数据库系统的所有用户都可以访问所有数据
以上的问题就是我们不想用代码解析文件来作为数据库的原因。
DBMS(Database Management System):是一个允许程序在无须关心底层实现的情况下,对数据库中的信息进行存储和分析的软件,它是一种能够被多种应用所复用的软件,这样就不必总是重复造轮子了。
一个通用DBMS旨在允许数据库的定义、创建、查询、更新和管理,它通常由一个互相关联的数据的集合和一组用以访问这些数据的程序组成。
关系模型(relational model)
关系模型用于抽象化数据库,而抽象化数据库用于避免以下维护:
- 首先把关系转化为简单的数据结构然后存入数据库,对于一张给出的表而言,所谓的关系实际上可以以元组的形式进行存储。
- 通过高级语言访问数据(应该将表跟表关联起来然后用高级语言访问表)
- 大型数据库的物理存储策略取决于数据库管理系统的实现,我们通过这些高级结构作为关系来定义我们的数据库,但实际上如何存储这些关系则取决于数据库系统的实现
关系模型不是唯一的数据模型,但它是最为广泛的数据模型,也可以说是最好的数据模型(老师观点)
DATA MODEL:
- Relational model
- KEY/VALUE
- Graph
- Document
- Column-family
- Array/Matrix
- Hierarchical
- NetWork
其中: key/value、graph、document、column-family都属于nosql,array/matrix属于Machine learning,剩下两种是属于Obsolete/Rare
Relational Model三要素:
- 关系结构(Structure):关系的定义及其内容,也就是说定义些什么、内容是什么、类型又是什么
- 完整性约束(Integrity):确保数据库的内容符合约束,然后根据你给出的结构以及schema,来定义为指定数据库的一个有效实例
- 操作(Manipulation):如何访问和修改数据库
关系是包含表示实体属性关系的无序集,我们使用元组来表达数据模型中的一条记录,例如:Artist(name, year, country),它是我们关系模型中的一个实体实例对应的属性集,元组是关系中的一组属性值(也称为它的域),在一开始由Ted Codd在1970年代所写的原始关系模型中,所有的值都必须保证原子性或者单个属性值,但是在最近可以在关系数据库中存入数组、json对象,限制可以存一个特殊值null。
主外键
主键:唯一标识,有的DBMS有提供自增等形式的主键
外键:用于指定一张表中的属性必须存在于另一张表中
DML(Data Manipulation languages)
DML是一种操作数据的方式,通过它可以用来访问或修改数据库来生成你想要的结果,它可以通过以下两种方式来访问:
- 程序(Procedural):通过制定一种高级策略来让DBMS来找到想要的结果。这也是我们学习的重点关系代数。
- 非程序(Non-Procedural):所执行的东西是我们希望数据库系统为我们生成的,但实际上我们不会去指定想要数据该如何生产并返回给我们。这属于关系演算,不是本课程的重点,但如果想要深入了解查询优化,那就需要学习关系演算。
关系代数
- Select
- Projection
- Union
- Intersection
- Difference
- Product
- Join
以上关系代数操作是属于集合操作,这种集合是一种数据的无序列表或无序集合。这里面的数据是可以重复的。我们将复杂的查询通过这些关系操作符链接在一起,以此来生成我们想要的答案。用下面给出的测试数据一一解释上诉七个代数。
关系R:
aId | bId |
---|---|
a1 | 101 |
a2 | 102 |
a3 | 103 |
a4 | 104 |
关系S:
aId | bId |
---|---|
a3 | 103 |
a4 | 104 |
a5 | 105 |
Select
它可以根据一些查询条件来得到符合条件的元组的子集,语法:
σ
p
r
e
d
i
c
a
t
e
(
R
)
\sigma_{predicate}(R)
σpredicate(R)
例如,有元组R(aId, bId):
σ
a
I
d
=
′
a
2
′
(
R
)
、
σ
a
I
d
=
′
a
2
′
a
n
d
b
I
d
>
102
(
R
)
\sigma_{aId='a2'}(R)、\sigma_{aId='a2' \ \ and \ \ bId>102}(R)
σaId=′a2′(R)、σaId=′a2′ and bId>102(R)
Projection
生成一个仅包含指定属性的新元组的关系,简单来说需要哪些字段就是投影
语法:
Π
A
1
,
A
2
,
.
.
.
,
A
n
(
R
)
\Pi_{A1,A2,...,An}(R)
ΠA1,A2,...,An(R)
例如:
Π
b
I
d
−
100
,
a
I
d
(
Σ
a
I
d
=
′
a
2
′
(
R
)
)
\Pi_{bId-100, aId}(\Sigma_{aId='a2'}(R))
ΠbId−100,aId(ΣaId=′a2′(R)),会生成如下数据
bId-100 | aId |
---|---|
2 | a2 |
3 | a2 |
用SQL来写
SELECT bId-100, aId From R where aId='a2'
Union
是将两个关系组合生成一个新的关系,这其中包含了这两个关系中的全部元组
语法:
R
∪
S
R \cup S
R∪S,输出如下:
aId | bId |
---|---|
a1 | 101 |
a2 | 102 |
a3 | 103 |
a3 | 103 |
a4 | 104 |
a5 | 105 |
Intersection
生成一个包含在两个关系中都出现过的元组的输出关系。
语法:
R
∩
S
R \cap S
R∩S,翻译成SQL就是:
(SELECT * FROM R)
INTERSECT
(SELECT * FROM S)
aId | bId |
---|---|
a3 | 103 |
Difference
语法:
R
−
S
R-S
R−S
生成一个仅在R中且不在S中的关系,结果如下:
aId | bId |
---|---|
a1 | 101 |
a2 | 102 |
Product
语法: R × S R \times S R×S
SELECT * FROM R CROSS JOIN S;
SELECT * FROM R,S
其实就是做笛卡尔积
R.aId | R.bId | S.aId | S.bId |
---|---|---|---|
a1 | 101 | a3 | 103 |
a1 | 101 | a4 | 104 |
a1 | 101 | a5 | 105 |
a2 | 102 | a3 | 103 |
a2 | 102 | a4 | 104 |
a2 | 102 | a5 | 105 |
a3 | 103 | a3 | 103 |
a3 | 103 | a4 | 104 |
a3 | 103 | a5 | 105 |
Join
生成一个包含两个元组有一个或多个共同值的关系
语法:
R
⋈
S
R \bowtie S
R⋈S
SELECT * FROM R NATURAL JOIN S;
结果如下:
aId | bId |
---|---|
a3 | 103 |
关系代数定义了如何计算查询的高级步骤,如下两个代数式,它们的最终结果是一致的,但是执行的效率不一致:
- σ b I d = 102 ( R ⋈ S ) \sigma_{bId=102}(R \bowtie S) σbId=102(R⋈S)
- R ⋈ ( σ b I d = 102 ( S ) ) R \bowtie (\sigma_{bId=102}(S)) R⋈(σbId=102(S))
如果R和S都有10亿条数据,是直接做自然连接然后再查询bId=102的数据,还是先查询bId=102的数据再做自然连接呢?这里其实就是涉及到了SQL优化。
自然连接
自然连接是一种特殊的等值连接,它要求两个关系表中进行连接的必须是相同的属性列(名字相同),无须添加连接条件,并且在结果中消除重复的属性列。
内连接(inner join, inner可省略)
内连接基本与自然连接相同,不同之处在于自然连接的是同名属性列的连接,而内连接则不要求两属性列同名,可以用using或on来指定某两列字段相同的连接条件。
Select * from table1 inner join table2 on table1.A=table2.E
左外连接(left outer join,outer可以省略)
左外连接是在两表进行自然连接,只把左表要舍弃的保留在结果集中,右表对应的列上填null。
Select * from table1 left outer join table2 on table1.C=table2.C
右外连接(rignt outer join,outer可以省略)
右外连接是在两表进行自然连接,只把右表要舍弃的保留在结果集中,左表对应的列上填null。
Select * from table1 right outer join table2 on table1.C=table2.C
全外连接(full outer join outer可以省略 )(mysql不支持)
全外连接是在两表进行自然连接,只把左表和右表要舍弃的都保留在结果集中,相对应的列上填null。
Select * from table1 full join table2 on table1.C=table2.C
mysql的全外连接可以使用union关键将左连接和右链接的结果合并
Select * from table1 left outer join table2 on table1.C=table2.C
UNION
Select * from table1 right outer join table2 on table1.C=table2.C