MySQL:主键、外键、索引(一)
干货:
主键是关系表中记录的唯一标识。主键的选取非常重要:主键不要带有业务含义,而应该使用BIGINT自增或者GUID类型。主键也不应该允许
NULL
。可以使用多个列作为联合主键,但联合主键并不常用。关系数据库通过外键可以实现一对多、多对多和一对一的关系。外键既可以通过数据库来约束,也可以不设置约束,仅依靠应用程序的逻辑来保证。
通过对数据库表创建索引,可以提高查询速度。通过创建唯一索引,可以保证某一列的值具有唯一性。数据库索引对于用户和应用程序来说都是透明的。
一、SQL(Structured Query Language)
数据模型:层次模型、网状模型、关系模型
数据类型:
关系型数据库:MYSQL
非关系型数据库:NoSQL、MongoDB、Cassandra、Dynamo
主流关系数据库:
- 商用数据库,例如:Oracle,SQL Server,DB2等;
- 开源数据库,例如:MySQL,PostgreSQL等;
- 桌面数据库,以微软Access为代表,适合桌面应用程序使用;
- 嵌入式数据库,以Sqlite为代表,适合手机应用和桌面程序。
SQL语言定义了三种操作数据库的能力:
DDL: Data Definition Language
DDL允许用户定义数据,即创建表、删除表、修改表结构这些操作。通常,DDL由数据库管理员执行
DML: Data Manipulation Language
DML为用户提供添加、删除、更新数据的能力,这些是应用程序对数据库的日常操作。
DQL: Data Query Language
DQL允许用户查询数据,这也是通常最频繁的数据库日常操作。
语法特点:SQL语言关键字不区分大小写,但是,针对不同的数据库,对于表名和列名,有的数据库区分大小写,有的数据库不区分大小写。同一个数据库,有的在Linux上区分大小写,有的在Windows上不区分大小写。
二、关系模型
关系数据库是建立在关系模型上的,而关系模型本质上是若干个存储数据的二维表,可以把它们看做很多Excel表。
表的每一行称为记录(Record),记录是一个逻辑意义上的数据;
表的每一列称为字段(Column),同一个表的每一行记录都拥有相同的若干字段。
字段定义了数据类型(整型、浮点型、字符串、日期等),以及是否允许为NULL
。注意NULL
表示字段数据不存在。一个整型字段如果为NULL
不表示它的值为0
,同样的,一个字符串型字段为NULL
也不表示它的值为空串''
。
和Excel表有所不同的是,关系数据库的表和表之间需要建立“一对多”,“多对一”和“一对一”的关系,这样才能够按照应用程序的逻辑来组织和存储数据。
1.主键
对于关系表,有个很重要的约束,就是任意两条记录不能重复。不能重复不是指两条记录不完全相同,而是指能够通过某个字段唯一区分出不同的记录,这个字段被称为主键。
选取主键的一个基本原则是:不使用任何业务相关的字段作为主键。
因此,身份证号、手机号、邮箱地址这些看上去可以唯一的字段,均不可用作主键。
作为主键最好是完全业务无关的字段,我们一般把这个字段命名为id
。常见的可作为id
字段的类型有:
- 自增整数类型:数据库会在插入数据时自动为每一条记录分配一个自增整数,这样我们就完全不用担心主键重复,也不用自己预先生成主键;
- 全局唯一GUID类型:使用一种全局唯一的字符串作为主键,类似
8f55d96b-8acc-4636-8cb8-76bf8abc2f57
。GUID算法通过网卡MAC地址、时间戳和随机数保证任意计算机在任意时间生成的字符串都是不同的,大部分编程语言都内置了GUID算法,可以自己预算出主键。
对于大部分应用来说,通常自增类型的主键就能满足需求。我们在students
表中定义的主键也是BIGINT NOT NULL AUTO_INCREMENT
类型。
如果使用INT自增类型,那么当一张表的记录数超过2147483647(约21亿)时,会达到上限而出错。使用BIGINT自增类型则可以最多约922亿亿条记录。
联合主键
关系数据库实际上还允许通过多个字段唯一标识记录,即两个或更多的字段都设置为主键,这种主键被称为联合主键。
对于联合主键,允许一列有重复,只要不是所有主键列都重复即可:
id_num | id_type | other columns... |
---|---|---|
1 | A | … |
2 | B | … |
3 | B | … |
如果我们把上述表的id_num
和id_type
这两列作为联合主键,那么上面的3条记录都是允许的,因为没有两列主键组合起来是相同的。
没有必要的情况下,我们尽量不使用联合主键,因为它给关系表带来了复杂度的上升。
2.外键
在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 students
DROP FOREIGN KEY fk_class_id;
注意:删除外键约束并没有删除外键这一列。删除列是通过DROP COLUMN ...
实现的。
多对多
通过一个表的外键关联到另一个表,我们可以定义出一对多关系。有些时候,还需要定义“多对多”关系。例如,一个老师可以对应多个班级,一个班级也可以对应多个老师,因此,班级表和老师表存在多对多关系。
多对多关系实际上是通过两个一对多关系实现的,即通过一个中间表,关联两个一对多关系,就形成了多对多关系。
一对一
一对一关系是指,一个表的记录对应到另一个表的唯一一个记录。
例如,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.索引
例如:
对于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
,因此,对该列创建索引就没有意义。
可以对一张表创建多个索引。索引的优点是提高了查询效率,缺点是在插入、更新和删除记录时,需要同时修改索引,因此,索引越多,插入、更新和删除记录的速度就越慢。
对于主键,关系数据库会自动对其创建主键索引。使用主键索引的效率是最高的,因为主键会保证绝对唯一。
唯一索引
在设计关系数据表的时候,看上去唯一的列,例如身份证号、邮箱地址等,因为他们具有业务含义,因此不宜作为主键。
但是,这些列根据业务要求,又具有唯一性约束:即不能出现两条记录存储了同一个身份证号。这个时候,就可以给该列添加一个唯一索引。例如,我们假设students
表的name
不能重复:
ALTER TABLE students
ADD UNIQUE INDEX uni_name (name);
通过UNIQUE
关键字我们就添加了一个唯一索引。
也可以只对某一列添加一个唯一约束而不创建唯一索引:
ALTER TABLE students
ADD CONSTRAINT uni_name UNIQUE (name);
MySQL:主键、外键、索引(一)的更多相关文章
- Mysql无法创建外键的原因
在Mysql中创建外键时,经常会遇到问题而失败,这是因为Mysql中还有很多细节需要我们去留意,我自己总结并查阅资料后列出了以下几种常见原因. 1. 两个字段的类型或者大小不严格匹配.例如,如果一个 ...
- 转!!!Mysql无法创建外键的原因
在Mysql中创建外键时,经常会遇到问题而失败,这是因为Mysql中还有很多细节需要我们去留意,我自己总结并查阅资料后列出了以下几种常见原因. 1. 两个字段的类型或者大小不严格匹配.例如,如果一个 ...
- (转)mysql 无法设置外键的原因总结
在Mysql中创建外键时,经常会遇到问题而失败,这是因为Mysql中还有很多细节需要我们去留意,我自己总结并查阅资料后列出了以下几种常见原因. 1. 两个字段的类型或者大小不严格匹配.例如,如果一个 ...
- 1、Mysql无法创建外键的原因 2、MySql 外键约束 之CASCADE、SET NULL、RESTRICT、NO ACTION分析和作用
在Mysql中创建外键时,经常会遇到问题而失败,这是因为Mysql中还有很多细节需要我们去留意,我自己总结并查阅资料后列出了以下几种常见原因. 1. 两个字段的类型或者大小不严格匹配.例如,如果一个 ...
- Mysql无法创建外键的原因 !!!
在MySQL中创建外键时,经常会遇到问题而失败,这是因为Mysql中还有很多细节需要我们去留意,我自己总结并查阅资料后列出了以下几种常见原因. 1. 两个字段的类型或者大小不严格匹配.例如,如果一个 ...
- Mysql学习总结(19)——Mysql无法创建外键的原因
在MySQL中创建外键时,经常会遇到问题而失败,这是因为Mysql中还有很多细节需要我们去留意,我自己总结并查阅资料后列出了以下几种常见原因. 1. 两个字段的类型或者大小不严格匹配.例如,如果一个 ...
- 整理Mysql无法创建外键的原因
在MySQL中创建外键时,经常会遇到问题而失败,这是因为mysql中还有很多细节需要我们去留意,我自己总结并查阅资料后列出了以下几种常见原因. 1. 两个字段的类型或者大小不严格匹配.例如,如果一个 ...
- [原创]MYSQL中利用外键实现级联删除和更新
MySQL中利用外键实现级联删除.更新 MySQL支持外键的存储引擎只有InnoDB,在创建外键的时候,要求父表必须有对应的索引,子表在创建外键的时候也会自动创建对应的索引.在创建索引的时候,可以指定 ...
- mysql之创建外键报150错误的处理方法
这几天由于在赶项目进度,也就没有及时记录下自己的学习情况 ,在完成项目的这段时间里,碰到了很多问题,在解决问题的过程中学习了不少技巧. 这里就主要介绍一下在mysql数据库中为表之间建立外键时报100 ...
- MySQL数据库建立外键失败的原因总结
在MySQL数据库创建外键时,经常会发生一些错误,这是一件很令人头疼的事.一个典型的错误就是:Can’t create table... 的错误.在很多实例中,这种错误的发生都是因为mysql一直以来 ...
随机推荐
- [LeetCode] 622.Design Circular Queue 设计环形队列
Design your implementation of the circular queue. The circular queue is a linear data structure in w ...
- spring boot 2X中@Scheduled实现定时任务及多线程配置
使用@Scheduled 可以很容易实现定时任务 spring boot的版本 2.1.6.RELEASE package com.abc.demo.common; import org.slf4j. ...
- JS核心系列:原型对象
在JS中,每当创建一个函数对象f1 时,该对象中都会内置一些属性,其中包括prototype和proto, prototype即原型对象. 每一个构造函数都有一个与之相关联的对象,该对象称之为原型对象 ...
- Re库入门
1. 正则表达式语法由字符和操作符构成 . 表示任何单个字符 [] 字符集,对单个字符给出取值范围 [abc]表示a.b.c,[a - z]表示a到z单个字符 [^] 非字符集,对单个字符给出排除 ...
- WPS应用技巧
打开云文档的文件:文件-打开-我的云文档 (选择时的文档为PDF时仅扫描PDF文件)
- Java byte和hex十六进制字符串转换
在Java中字符串由字符char组成, 一个char由两个byte组成, 而一个byte由八个bit组成, 一个十六进制字符(0-F)实际上由4个字节byte即可表达, 因此, 从字节数组到十六进制字 ...
- SQL Server 使用文件组备份降低备份文件占用的存储空间
对于DBA来说,备份和刷新简历是最重要的两项工作,如果发生故障后,发现备份也不可用,那么刷新简历的重要性就显现出来,哇咔咔!当然备份是DBA最重要的事情(没有之一),在有条件的情况下,我们应该在多个服 ...
- 玩转 SpringBoot 2 快速整合 Filter
概述 SpringBoot 中没有 web.xml, 我们无法按照原来的方式在 web.xml 中配置 Filter .但是我们可以通过 JavaConfig(@Configuration +@Bea ...
- c++ pipe实现父子进程通信
1.父子进程通信pipe编程流程 -创建管道 -设置进程的输出到管道 -创建进程 -关闭管道写句柄 -读管道读句柄,把数据读到一个buffer里 2.注意事项 -读管道数据的时候,一定要关闭写句柄: ...
- JAVA知识点总结篇(二)
数组 一维数组 声明 数据类型[] 数组名: 数据类型 数组名[]: 分配空间 数组名 = new 数据类型 [数组长度]: 可以在声明的同时分配空间,分配空间之后数组中才能放数据,数组元素都是通过下 ...