数据库粗略回顾(一)

本科阶段已经学习过一个学期的数据库,但是很多理论性的知识因为很久不用所以忘得差不多了,现特抽出几天时间简单回顾下。

一、关系数据库概述

数据库作为一种专门管理数据的软件。应用程序不需要自己管理数据,而是通过数据库软件提供的接口来读写数据。至于数据本身如何存储到文件,那是数据库软件的事情,应用程序自己并不关心:

┌──────────────┐
│ application  │
└──────────────┘
       ▲│
       ││
   read││write
       ││
       │▼
┌──────────────┐
│   database   │
└──────────────┘

这样一来,编写应用程序的时候,数据读写的功能就被大大地简化了。

数据模型

三种

  • 层次模型
  • 网状模型
  • 关系模型

层次模型就是以“上下级”的层次关系来组织数据的一种方式,层次模型的数据结构看起来就像一颗树:

            ┌─────┐
            │     │
            └─────┘
               │
       ┌───────┴───────┐
       │               │
    ┌─────┐         ┌─────┐
    │     │         │     │
    └─────┘         └─────┘
       │               │
   ┌───┴───┐       ┌───┴───┐
   │       │       │       │
┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐
│     │ │     │ │     │ │     │
└─────┘ └─────┘ └─────┘ └─────┘

网状模型把每个数据节点和其他很多节点都连接起来,它的数据结构看起来就像很多城市之间的路网:

     ┌─────┐      ┌─────┐
   ┌─│     │──────│     │──┐
   │ └─────┘      └─────┘  │
   │    │            │     │
   │    └──────┬─────┘     │
   │           │           │
┌─────┐     ┌─────┐     ┌─────┐
│     │─────│     │─────│     │
└─────┘     └─────┘     └─────┘
   │           │           │
   │     ┌─────┴─────┐     │
   │     │           │     │
   │  ┌─────┐     ┌─────┐  │
   └──│     │─────│     │──┘
      └─────┘     └─────┘

关系模型把数据看作是一个二维表格,任何数据都可以通过行号+列号来唯一确定,它的数据模型看起来就是一个Excel表

┌─────┬─────┬─────┬─────┬─────┐
│     │     │     │     │     │
├─────┼─────┼─────┼─────┼─────┤
│     │     │     │     │     │
├─────┼─────┼─────┼─────┼─────┤
│     │     │     │     │     │
├─────┼─────┼─────┼─────┼─────┤
│     │     │     │     │     │
└─────┴─────┴─────┴─────┴─────┘

数据类型

对于一个关系表,除了定义每一列的名称外,还需要定义每一列的数据类型。关系数据库支持的标准数据类型包括数值、字符串、时间等:

名称                  类型            说明

INT                    整型            4字节整数类型,范围约+/-21亿
BIGINT             长整型           8字节整数类型,范围约+/-922亿亿
REAL                浮点型           4字节浮点数,范围约+/-1038
DOUBLE                浮点型           8字节浮点数,范围约+/-10308
DECIMAL(M,N)        高精度小数    由用户指定精度的小数,例如,DECIMAL(20,10)表示一共20位,其中小数10位,通常用于财务计算
CHAR(N)    定长字符串    存储指定长度的字符串,例如,CHAR(100)总是存储100个字符的字符串
VARCHAR(N)            变长字符串    存储可变长度的字符串,例如,VARCHAR(100)可以存储0~100个字符的字符串
BOOLEAN                布尔类型    存储True或者False
DATE                日期类型    存储日期,例如,2018-06-22
TIME                时间类型    存储时间,例如,12:20:59
DATETIME            日期和时间类型    存储日期+时间,例如,2018-06-22 12:20:59

上述是常用数据类型,很多数据类型还有别名,REAL ——> FLOAT(24), 还有一些不常用的数据类型TINYINT(0~255)、JSON等等。

选择数据类型的时候,要根据业务规则选择合适的类型。通常来说,BIGINT能满足整数存储的需求,VARCHAR(N)能满足字符串存储的需求,这两种类型是使用最广泛的。

主流数据库介绍

目前,主流的关系数据库主要分为以下几类:
1.商用数据库,例如:Oracle,SQL Server,DB2等;
2.开源数据库,例如:MySQL,PostgreSQL等;
3.桌面数据库,以微软Access为代表,适合桌面应用程序使用;
4.嵌入式数据库,以Sqlite为代表,适合手机应用和桌面程序。

SQL介绍

SQL是结构化查询语言的缩写,用来访问和操作数据库系统。不同的数据库,都支持SQL。

各个不同的数据库对标准的SQL支持不太一致。并且,大部分数据库都在标准的SQL上做了扩展。也就是说,如果只使用标准SQL,理论上所有数据库都可以支持,但如果使用某个特定数据库的扩展SQL,换一个数据库就不能执行了。例如,Oracle把自己扩展的SQL称为PL/SQL,Microsoft把自己扩展的SQL称为T-SQL。

现实情况是,如果我们只使用标准SQL的核心功能,那么所有数据库通常都可以执行。不常用的SQL功能,不同的数据库支持的程度都不一样。而各个数据库支持的各自扩展的功能,通常我们把它们称之为“方言”。

总的来说,SQL语言定义了这么几种操作数据库的能力:

DDL:Data Definition Language
DDL允许用户定义数据,也就是创建表、删除表、修改表结构这些操作。通常,DDL由数据库管理员执行。

DML:Data Manipulation Language
DML为用户提供添加、删除、更新数据的能力,这些是应用程序对数据库的日常操作。

DQL:Data Query Language
DQL允许用户查询数据,这也是通常最频繁的数据库日常操作。

语法特点

SQL语言关键字不区分大小写,但一般 我们规定SQL关键字大写,表名、列名均使用小写。


二、安装MySQL

开源关系数据库,现Oracle 旗下产品

和其他关系数据库有所不同的是,MySQL本身实际上只是一个SQL接口,它的内部还包含了多种数据引擎,常用的包括:

  • InnoDB:由Innobase Oy公司开发的一款支持事务的数据库引擎,2006年被Oracle收购;
  • MyISAM:MySQL早期集成的默认数据库引擎,不支持事务。

MySQL接口和数据库引擎的关系就好比某某浏览器和浏览器引擎(IE引擎或Webkit引擎)的关系。对用户而言,切换浏览器引擎不影响浏览器界面,切换MySQL引擎不影响自己写的应用程序使用MySQL的接口。

使用MySQL时,不同的表还可以使用不同的数据库引擎。如果你不知道应该采用哪种引擎,记住总是选择InnoDB就好了。

基于MySQL的衍生版本:MariaDB、Aurora、PolarDB .

MySQL官方版版本:

  • Community Edition:社区开源版本,免费;

  • Standard Edition:标准版;

  • Enterprise Edition:企业版;

  • Cluster Carrier Grade Edition:集群版。

     基本SQL功能相同

安装MySQL

网 址:https://dev.mysql.com/downloads/mysql/

Debian和Ubuntu用户可以简单地通过命令

apt-get install mysql-server

安装最新的MySQL版本。

运行MySQL

MySQL安装后会自动在后台运行。为了验证MySQL安装是否正确,我们需要通过mysql这个命令行程序来连接MySQL服务器。

在命令提示符下输入mysql -u root -p,然后输入口令,如果一切正确,就会连接到MySQL服务器,同时提示符变为mysql>。

输入exit退出MySQL命令行。注意,MySQL服务器仍在后台运行。


三、关系模型

关系数据库是建立在关系模型上,而关系模型本质上就是若干个存储数据的二维表,可以把它们看作很多Excel表。

表的每一行称为记录(Record),记录是一个逻辑意义上的数据。

表的每一列称为字段(Column),同一个表的每一行记录都拥有相同的若干字段。

字段定义了数据类型(整型、浮点型、字符串、日期等),以及是否允许为NULL。注意NULL表示字段数据不存在。一个整型字段如果为NULL不表示它的值为0,同样的,一个字符串型字段为NULL也不表示它的值为空串’’。

通常情况下,字段应该避免允许为NULL。不允许为NULL可以简化查询条件,加快查询速度,也利于应用程序读取数据后无需判断是否为NULL。

和Excel表有所不同的是,关系数据库的表和表之间需要建立“一对多”,“多对一”和“一对一”的关系,这样才能够按照应用程序的逻辑来组织和存储数据。

例如,一个班级表:

ID         名称              班主任
201        二年级一班          王老师
202        二年级二班        李老师

每一行对应着一个班级,而一个班级对应着多个学生,所以班级表和学生表的关系就是“一对多”

ID    姓名    班级ID    性别    年龄
1    小明    201      M       9
2    小红    202         F       8
3    小军    202         M       8
4    小白    201     F       9

反过来,如果我们先在学生表中定位了一行记录,例如ID=1的小明,要确定他的班级,只需要根据他的“班级ID”对应的值201找到班级表中ID=201的记录,即二年级一班。所以,学生表和班级表是“多对一”的关系。

如果我们把班级表分拆得细一点,例如,单独创建一个教师表:

ID        名称        年龄
A1        王老师        26
A2        张老师        39
A3        李老师      32
A4        赵老师      27

班级表只存储教师ID:

ID        名称            班主任ID
201        二年级一班         A1
202     二年级二班        A3

这样,一个班级总是对应一个教师,班级表和教师表就是“一对一”关系

在关系数据库中,关系是通过主键和外键来维护的。我们在后面会分别深入讲解。


3.1.1 主键

对于关系表,有个很重要的约束,就是任意两条记录不能重复。不能重复不是指两条记录不完全相同,而是指能够通过某个字段唯一区分出不同的记录,这个字段被称为主键

例如,students表的两行记录:

id    class_id    name    gender    score
1    1            小明        M        90
2    1            小红        F        95

假设我们把name字段作为主键,那么通过名字小明或小红就能唯一确定一条记录。但是,这么设定,就没法存储同名的同学了,因为插入相同主键的两条记录是不被允许的

对主键的要求,最关键的一点是:记录一旦插入到表中,主键最好不要再修改,因为主键是用来唯一定位记录的,修改了主键,会造成一系列的影响。

由于主键的作用十分重要,如何选取主键会对业务开发产生重要影响。如果我们以学生的身份证号作为主键,似乎能唯一定位记录。然而,身份证号也是一种业务场景,如果身份证号升位了,或者需要变更,作为主键,不得不修改的时候,就会对业务产生严重影响。

所以,选取主键的一个基本原则是:不使用任何业务相关的字段作为主键。

因此,身份证号、手机号、邮箱地址这些看上去可以唯一的字段,均不可用作主键。

作为主键最好是完全业务无关的字段,我们一般把这个字段命名为id。常见的可作为id字段的类型有:

  1. 自增整数类型:数据库会在插入数据时自动为每一条记录分配一个自增整数,这样我们就完全不用担心主键重复,也不用自己预先生成主键;

2.全局唯一GUID类型:使用一种全局唯一的字符串作为主键,类似8f55d96b-8acc-4636-8cb8-76bf8abc2f57。GUID算法通过网卡MAC地址、时间戳和随机数保证任意计算机在任意时间生成的字符串都是不同的,大部分编程语言都内置了GUID算法,可以自己预算出主键。

对于大部分应用来说,通常自增类型的主键就能满足需求。我们在students表中定义的主键也是BIGINT NOT NULL AUTO_INCREMENT类型。

如果使用INT自增类型,那么当一张表的记录数超过2147483647(约21亿)时,会达到上限而出错。使用BIGINT自增类型则可以最多约922亿亿条记录。

3.1.2 联合主键

关系数据库实际上还允许通过多个字段唯一标识记录,即两个或更多的字段都设置为主键,这种主键被称为联合主键。

对于联合主键,允许一列主键有重复,只要不是所有主键列都重复即可:

 id_num              id_type            other columns...
    1               A                       ...
    2                A                        ...
    2                B                       ...

如果我们把上述表的id_num和id_type这两列作为联合主键,那么上面的3条记录都是允许的,因为没有两列主键组合起来是相同的。

没有必要的情况下,我们尽量不使用联合主键,因为它给关系表带来了复杂度的上升。

3.1.3 小结

主键是关系表中记录的唯一标识。主键的选取非常重要:主键不要带有业务含义,而应该使用BIGINT自增或者GUID类型。主键也不应该允许NULL。

可以使用多个列作为联合主键,但联合主键并不常用。


3.2.1 外键

当我们用主键唯一标识记录时,我们就可以在 students 表中确定任意一个学生的记录:

id    name    other columns...
1    小明        ...
2    小红        ...

我们还可以在 classes 表中确定任意一个班级记录:

id    name    other columns...
1    一班    ...
2    二班    ...

但是我们如何确定students表的一条记录,例如,id=1的小明,属于哪个班级呢?

由于一个班级可以有多个学生,在关系模型中,这两个表的关系可以称为“一对多”,即一个classes的记录可以对应多个students表的记录。

为了表达这种一对多的关系,我们需要在students表中加入一列class_id,让它的值与classes表的某条记录相对应:

id    class_id    name    other columns...
1        1        小明        ...
2        1       小红        ...
5        2        小白        ...

这样,我们就可以根据class_id这个列直接定位出一个students表的记录应该对应到classes的哪条记录。

在students表中,通过class_id的字段,可以把数据与另一张表关联起来,这种列称为外键

外键并不是通过列名实现的,而是通过定义外键约束实现的:

ALTER TABLE students
ADD CONSTRAINT fk_class_id
FOREIGN KEY (class_id)
REFERENCES classes (id);

其中,外键约束的名称fk_class_id可以任意,FOREIGN KEY (class_id)指定了class_id作为外键,REFERENCES classes (id)指定了这个外键将关联到classes表的id列(即classes表的主键)。

通过定义外键约束,关系数据库可以保证无法插入无效的数据。即如果classes表不存在id=99的记录,students表就无法插入class_id=99的记录。

由于外键约束会降低数据库的性能,大部分互联网应用程序为了追求速度,并不设置外键约束,而是仅靠应用程序自身来保证逻辑的正确性。这种情况下,class_id仅仅是一个普通的列,只是它起到了外键的作用而已。

要删除一个外键约束,也是通过ALTER TABLE实现的:

ALTER TABLE students
DROP FOREIGN KEY fk_class_id;

注意:删除外键约束并没有删除外键这一列。删除列是通过DROP COLUMN …实现的。

3.2.2 多对多

通过一个表的外键关联到另一个表,我们可以定义出一对多关系。有些时候,还需要定义“多对多”关系。例如,一个老师可以对应多个班级,一个班级也可以对应多个老师,因此,班级表和老师表存在多对多关系。

多对多关系实际上是通过两个一对多关系实现的,即通过一个中间表,关联两个一对多关系,就形成了多对多关系:

teachers表:

id    name
1    张老师
2    王老师
3    李老师
4    赵老师

classes表:

id    name
1    一班
2    二班

中间表teacher_class关联两个一对多关系:

id    teacher_id    class_id
1        1           1
2       1           2
3        2            1
4        2            2
5        3            1
6        4            2

通过中间表teacher_class可知teachers到classes的关系:

  • id=1的张老师对应id=1,2的一班和二班;
  • id=2的王老师对应id=1,2的一班和二班;
  • id=3的李老师对应id=1的一班;
  • id=4的赵老师对应id=2的二班。

同理可知classes到teachers的关系:

  • id=1的一班对应id=1,2,3的张老师、王老师和李老师;
  • id=2的二班对应id=1,2,4的张老师、王老师和赵老师;

因此,通过中间表,我们就定义了一个“多对多”关系。

3.2.3 一对一

一对一关系是指,一个表的记录对应到另一个表的唯一一个记录。

例如,students表的每个学生可以有自己的联系方式,如果把联系方式存入另一个表contacts,我们就可以得到一个“一对一”关系:

id        student_id        mobile
1           1          135xxxx6300
2            2          138xxxx2209
3            5          139xxxx8086

既然是一对一关系,那为啥不给students表增加一个mobile列,这样就能合二为一了???

如果业务允许,完全可以把两个表合为一个表。但是,有些时候,如果某个学生没有手机号,那么,contacts表就不存在对应的记录。实际上,一对一关系准确地说,是contacts表一对一对应students表。

有一些应用会把一个大表拆成两个一对一的表,目的是把经常读取和不经常读取的字段分开,以获得更高的性能。例如,把一个大的用户表分拆为用户基本信息表user_info和用户详细信息表user_profiles,大部分时候,只需要查询user_info表,并不需要查询user_profiles表,这样就提高了查询速度。

3.2.4 小结

关系数据库通过外键可以实现一对多、多对多和一对一的关系。外键既可以通过数据库来约束,也可以不设置约束,仅依靠应用程序的逻辑来保证。

3.3.1 索引

在关系数据库中,如果有上万甚至上亿条记录,在查找记录的时候,想要获得非常快的速度,就需要使用索引。

索引关系数据库中对某一列或多个列的值进行预排序的数据结构。通过使用索引,可以让数据库系统不必扫描整个表,而是直接定位到符合条件的记录,这样就大大加快了查询速度

例如,对于students表:

id        class_id        name    gender        score
1            1           小明        M            90
2            1            小红        F           95
3           1           小军        M            88

如果要经常根据score列进行查询,就可以对score列创建索引:

ALTER TABLE students
ADD INDEX idx_score (score);

使用 ADD INDEX idx_score (score) 就创建了一个名称为 idx_score ,使用列 score 的索引。索引名称是任意的,索引如果有多列,可以在括号里依次写上,例如:

ALTER TABLE students
ADD INDEX idx_name_score (name, score);

索引的效率取决于索引列的值是否散列,即该列的值如果越互不相同,那么索引效率越高。反过来,如果记录的列存在大量相同的值,例如gender列,大约一半的记录值是 M,另一半是 F,因此,对该列创建索引就没有意义。

可以对一张表创建多个索引。索引的点:提高了查询效率,缺点:在插入、更新和删除记录时,需要同时修改索引,因此,索引越多,插入、更新和删除记录的速度就越慢。

对于主键,关系数据库会自动对其创建主键索引。使用主键索引的效率是最高的,因为主键会保证绝对唯一。

3.3.2 唯一索引

在设计关系数据表的时候,看上去唯一的列,例如身份证号、邮箱地址等,因为他们具有业务含义,因此不宜作为主键。

但是,这些列根据业务要求,又具有唯一性约束:即不能出现两条记录存储了同一个身份证号。这个时候,就可以给该列添加一个唯一索引。例如,我们假设students表的name不能重复:
(即不宜作主键,宜作索引)

ALTER TABLE students
ADD UNIQUE INDEX uni_name (name);

通过UNIQUE关键字我们就添加了一个唯一索引。
也可以只对某一列添加一个唯一约束而不创建唯一索引

ALTER TABLE students
ADD CONSTRAINT uni_name UNIQUE (name);

这种情况下,name列没有索引,但仍然具有唯一性保证。

无论是否创建索引,对于用户和应用程序来说,使用关系数据库不会有任何区别。这里的意思是说,当我们在数据库中查询时,如果有相应的索引可用,数据库系统就会自动使用索引来提高查询效率,如果没有索引,查询也能正常执行,只是速度会变慢。因此,索引可以在使用数据库的过程中逐步优化。

3.3.3 小结

通过对数据库表创建索引,可以提高查询速度。

通过创建唯一索引,可以保证某一列的值具有唯一性。

数据库索引对于用户和应用程序来说都是透明的



   转载规则


《数据库粗略回顾(一)》 Adward 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
数据库粗略回顾(二) 数据库粗略回顾(二)
四、查询数据4.1 基本查询要查询数据库表的数据,我们使用如下的SQL语句: SELECT * FROM <表名>假设表名是students,要查询students表的所有行,我们用如下SQL语句: SELECT * F
2020-03-03
下一篇 
十大经典排序算法整理汇总(附代码) 十大经典排序算法整理汇总(附代码)
本文整理并总结了十大经典的排序算法(冒泡排序、选择排序、插入排序、快速排序、归并排序、希尔排序、计数排序、基数排序、桶排序、堆排序)的时间复杂度、空间复杂度等性质。
2020-02-16
  目录