SQL不区分大小写(包括字段名),以“;”结尾,字符串统一用单引号括起来,双引号在mysql中可以用,但是不能在Oracle中使用。
常用命令
exit
show databases;
show tables;
use 数据库名;
create database 数据库名;
select * from 表名; 显示表中的所有数据
desc 表名; 显示表的结构
select version();
select database; 查看当前使用的是哪个数据库
行row:被称为数据/记录;
列column:字段
DQL数据查询语言
- 凡是带有select关键字的
简单查询
查询一个字段: select 字段名 from 表名;
查询多个字段: select 字段名1,字段名2 from 表名;
查询所有字段: select * from 表名; (效率低,不建议使用)
修改列的名字:select 要修改的字段名 as 新字段名 from 表名;(只是显示的列名变了,原表不变)as可以省略,如果字段名中有空格,加单引号
select语句永远不会进行修改操作,只负责查询
列参与数学操作+ - * /:select 列名12 as 新列名 from 表名; 不as新列名的话,列名会显示为 “列名12”
条件查询
- select 列名 from 表名 where 条件;
=、< 、<=、 >、 >=
不等于:!=或者<>
两个值之间:between … and …(必须遵循左小右大,闭区间);等同于 >= and <=;
is null; is not null;(在数据库中不能用“=”衡量)
and
or
and优先级大于or
in ();括号中不是区间,是具体的值
not in
like:模糊查询,支持%或下划线匹配,%匹配任意字符,下划线任意一个字符;
找出名字中含有“o”的:select ename from emp where ename like ‘%o%’;
找出名字中含有“_”的:select ename from emp where ename like ‘% \ _ %’; ( \ 转义字符)
排序
select
字段名
from
表名
order by
字段名; //默认升序,降序在后面加 desc
//多字段排序:字段名1 指定降序or升序,字段名2 指定降序or升序;
//字段1主导,字段1相同时,才启用字段2排序;
//根据字段位置位置排序
order by 2 ;//表示按照第二列排序
指定降序desc
指定升序asc
数据处理函数/单行处理函数
一个输入对应一个输出;
lower: 转换小写
select lower(字段名) from 表名;
upper: 转换大写
select * from 表名 where job=upper(‘manager’);
substr: 取子串
substr(被截取的字段,起始下标,截取的长度) //起始下标从1开始,如substr(ename,1,1);
length: 取长度
trim: 去空格
str_to_date:字符串转换为date类型
str_to_date(‘1981-02-20’,’%Y-%m-%d’);//str_to_date(字符串,匹配格式),
%Y代表4位的年份,%y代表2位的年份;
%m代表01,02月;%c代表1,2…12;
%H代表24小时制00,…,23;%h表示12小时制01,…,12;
%i分钟00,…,59;
%r 代表时间,12小时制,(hh:mm:ss[AP]M);
%T 代表时间,24小时制,(hh:mm:ss);
%S代表秒,格式为(00,…,59);
%s代表秒,格式为(00,…,59);
date_format:格式化日期
date_farmat(字段名,匹配格式)
format:设置千分位
format(字段名,保留小数位)
round:四舍五入
round(数值或字段名,保留小数位),-1表示保留到10位,依次类推
rand():生成随机数
随机抽取记录数:select * from emp order by rand()limit 2;
ifnull:将null转为一个具体值
ifnull(字段名,替换值)
在数据库中,只要有null参与运算,结果就是null;
concat:进行字符串拼接
case…when…then…when…then…else…end
select
字段名
case
字段名
when
条件内容1
then
条件内容1为真,执行这个语句
else
条件内容1为假,执行这个语句
end
语句
select
empno,ename,job,sal,
case job
when 'MANAGER'
then sal*1.1
when 'SALESMAN'
then sal*1.5
end as newsal from emp;
分组函数/聚合函数/多行处理函数
分组函数自动忽略null
分组函数不能直接使用在where子句中
所有分组函数可以组合一起用
count:取得记录数;
select count(*) from emp where xxx; 符合条件的所有记录;
select count(字段名) from emp; 字段中不为空的元素总数;
select count(distinct 字段名) from emp; 字段中set后的个数
sum:求和;
avg:取平均;
max:取最大值;
min:取最小值;
分组查询(非常重要)
- 在SQL 语句中若有 group by 语句,那么在 select 语句后面只能跟 分组函数 参与分组的字段
group by
select
字段名
from
表名
group by
用来分组的字段
having:就是为了过滤分组后的数据 而存在的 不可以单独的出现;
如果相对分组数据再继续过滤,需要使用having子句;
select deptno,max(sal) from emp group by deptno having max(sal)>3000;
distinct
显示的时候去除重复数据;
出现在多个字段之前,表示多个字段联合起来去重;
select distinct 字段名 from 表名;
连接查询(最重要)
- 从多张表中联合起来查询数据,称为单表查询;
- 表连接的方式:
内连接:等值连接、非等值连接、自连接;特点:查询出完全可以匹配条件的数据;inner可以省略
外连接:左连接、右连接;outer可以省略
全连接(不常用)
内连接之等值连接
sql92
select
e.ename,d.dname
from
emp e,dept d
where
e.deptno = d.deptno;
sql99
select
e.ename,d.dname
from
emp e
join
dept d
on
e.deptno = d.deptno;
sql99语法
select
---
from
表1
join
表2
on
表连接的条件
where
...
内连接之非等值连接
select
e.ename,e.sal,s.grade
from
emp e
join
salgrade s
on
e.sal between s.losal and hisal;
内连接之自连接
把一张表看成两张表,分别起别名
select
e.ename,m.ename
from
emp e
join
emp m
on
e.mgr=m.empno;
外连接
- 在外连接中,两张表产生了主次关系;
- 右连接:将join右边的表作为主表,主要将主表中的所有数据显示出来,捎带关联查询左边的表;
- 左连接:将join左边的表作为主表,主要将主表中的所有数据显示出来,捎带关联查询右边的表;
select
e.name,d.name
from
emp e right join dept d
on
e.deptno = d.deptno;
多张表的连接
select
...
from
a
join
b
on
a和b的连接条件
join
c
on
a和c的连接条件
join
d
on
a和d的连接条件
子查询
- 子查询就是嵌套的select语句,可以理解为是一张表;
select
..(select)
from
..(select)
where
..(select)
where中使用子查询
//找出比平均工资低的员工姓名和工资
select
ename,sal
from
emp
where sal > (select avg(sal) from emp );
from中使用子查询
- from后面的子查询,可以将子查询后面的查询结果当作一张临时表;
select
t.* ,s.grade
from
(select job,avg(sal) as a from emp group by job) t
join
salgrade s
on
t.a between s.losal and s.hisal;
select中使用子查询
- select后面的子查询只能一次返回一条结果,多于一条会报错;
//找出每个员工的部门名称,要求显示员工名,部门名
select
e.name,(select d.dname from dept d where e.deptno = d.deptno) as dname
from
emp e;
union
- 将查询结果合并,要求两个结果的列数和数据类型一致;
select ename,job from emp where job='MANAGER'
union
select ename,job from emp where job = 'SALESMAN';
union的效率要高一些。对于表连接来说,每连接一次新表,则匹配的次数满足笛卡尔积,成倍的翻。。。
但是union可以减少匹配的次数。在减少匹配次数的情况下,还可以完成两个结果集的拼接。
limit
- 将查询结果集的一部分取出来。通常使用在分页查询当中。
//按照薪资降序,取出排名在前5名的员工?
select
ename,sal
from
emp
order by
sal desc
limit 0,5;// 完整用法limit startIndex(起始下标从0开始),length;缺省用法limit 条数;取前数条;
DQL顺序
- 关键字输入顺序:select…from…where…group by…having…order by…limit
- 关键字执行顺序:1.from 2.where 3.group by 4.having 5.select 6.order by 7.limit
DML数据操作语言
- 凡是对表中的数据进行增删改查的
insert
insert into 表名(字段名1,字段名2,字段名3…) values(值1,值2,值3);
字段名和数据类型要一 一对应;可以和表中的表头顺序一模一样,也可指插入部分字段,其他字段默认用null填充;
可以省略字段名,要求values的值和表头中的字段全部一 一匹配;
insert语句但凡是执行成功了,那么必然会多一条记录。
str_to_date:将字符串varchar类型转换成date类型
通常使用在插入insert方面,因为插入的时候需要一个日期类型的数据,需要通过该函数将字符串转换成date。
date_format:将date类型转换成具有一定格式的varchar字符串类型。
这个函数通常使用在查询日期方面。设置展示的日期格式。
注意:数据库中的有一条命名规范:所有的标识符都是全部小写,单词和单词之间使用下划线进行衔接。如果你提供的日期字符串是‘%Y-%m-%d’这个格式,str_to_date函数就不需要了;
插入多条数据
insert into
表名(字段名1,字段名2,字段名3...)
values
(值1,值2,值3),
(值1,值2,值3),
...
;
将查询结果插入到一张表中
insert into dept_bak select * from dept; //很少用!
删除表drop
drop table 表名;//表不存在会报错;
drop table if exists 表名;
删除数据delete
delete from 表名 where 条件;
注意:没有条件,整张表的数据会全部删除!
快速删除表中的数据
- delete语句删除数据的原理?(delete属于DML语句!!!)
表中的数据被删除了,但是这个数据在硬盘上的真实存储空间不会被释放;
缺点:删除效率比较低。
优点:支持回滚rollback,后悔了可以再恢复数据。 - truncate语句删除数据的原理?(这种操作属于DDL操作。)
这种删除效率比较高,表被一次截断,物理删除。
缺点:不支持回滚。
优点:快速。
truncate是删除表中的数据,表还在!
truncate table 表名;
修改update
update 表名 set 字段名1=值1,字段名2=值2,字段名3=值3… where 条件;
注意:没有条件限制会导致所有数据全部更新。
事务(非常重要)
一个事务其实就是一个完整的业务逻辑。是一个最小的工作单元。不可再分。要求必须同时成功或者同时失败。
只有DML语句才会有事务这一说,其它语句和事务无关。insert、delete、update只有以上的三个语句和事务有关系,其它都没有关系。
- 事务是怎么做到多条DML语句同时成功和同时失败
InnoDB存储引擎:提供一组用来记录事务性活动的日志文件
在事务的执行过程中,每一条DML的操作都会记录到“事务性活动的日志文件”中。
在事务的执行过程中,我们可以提交事务,也可以回滚事务。 - 提交事务commit:清空事务性活动的日志文件,将数据全部彻底持久化到数据库表中。提交事务标志着,事务的结束。并且是一种全部成功的结束。
- 回滚事务rollback:将之前所有的DML操作全部撤销,并且清空事务性活动的日志文件,回滚事务标志着,事务的结束。并且是一种全部失败的结束。
mysql默认情况下是支持自动提交事务的,每执行一条DML语句,则提交一次!
关闭自动提交机制:start transaction;
事务的四个特性:
- A原子性:说明事务是最小的工作单元。不可再分。
- C一致性:所有事务要求,在同一个事务当中,所有操作必须同时成功,或者同时失败;以保证数据的一致性。
- I隔离性:A事务和B事务之间具有一定的隔离;教室A和教室B之间有一道墙,这道墙就是隔离性;A事务在操作一张表的时候,另一个事务B也操作这张表会那样???
- D持久性:事务最终结束的一个保障。事务提交,就相当于将没有保存到硬盘上的数据;保存到硬盘上!
事务的隔离性
隔离级别:
- 读未提交read uncommitted《没有提交就读到了》:事务A可以读取到事务B未提交的数据,存在脏读现象;
- 读已提交read committed《提交之后才能读到》:事务A只能读取到事务B提交之后的数据,解决了脏读的现象。不可重复读取数据。oracle数据库默认的隔离级别;
- 可重复读repeatable read《提交之后也读不到,永远读取的都是刚开启事务时的数据》:事务A开启之后,不管是多久,每一次在事务A中读取到的数据都是一致的。即使事务B将数据已经修改,并且提交了,事务A读取到的数据还是没有发生改变,这就是可重复读。解决了不可重复读取数据。可能会出现幻影读,每一次读取到的数据都是幻象,不够真实。mysql中默认的事务隔离级别
- 序列化/串行化serializable(最高的隔离级别):这种隔离级别表示事务排队,不能并发;synchronized,线程同步(事务同步);每一次读取到的数据都是最真实的,并且效率是最低的。
- 查看隔离级别:SELECT @@tx_isolation
- 设置隔离级别:set global transaction isolation level read uncommitted;
索引
索引是在数据库表的字段上添加的,是为了提高查询效率存在的一种机制。
一张表的一个字段可以添加一个索引,当然,多个字段联合起来也可以添加索引。
索引相当于一本书的目录,是为了缩小扫描范围而存在的一种机制。
如果name字段上没有添加索引(目录),或者说没有给name字段创建索引,MySQL会进行全扫描,会将name字段上的每一个值都比对一遍。效率比较低。
MySQL在查询方面主要就是两种方式:第一种方式:全表扫描;第二种方式:根据索引检索。
在mysql数据库当中索引也是需要排序的,并且这个所以的排序和TreeSet数据结构相同。TreeSet(TreeMap)底层是一个自平衡的二叉树!在mysql当中索引是一个B-Tree数据结构。
提醒1:在任何数据库当中主键上都会自动添加索引对象,id字段上自动有索引,因为id是PK。另外在mysql当中,一个字段上如果有unique约束的话,也会自动创建索引对象。
提醒2:在任何数据库当中,任何一张表的任何一条记录在硬盘存储上都有一个硬盘的物理存储编号。
提醒3:在mysql当中,索引是一个单独的对象,不同的存储引擎以不同的形式存在,在MyISAM存储引擎中,索引存储在一个.MYI文件中。在InnoDB存储引擎中索引存储在一个逻辑名称叫做tablespace的当中。在MEMORY存储引擎当中索引被存储在内存当中。不管索引存储在哪里,索引在mysql当中都是一个树的形式存在。(自平衡二叉树:B-Tree)
- 什么条件下,我们会考虑给字段添加索引呢?
条件1:数据量庞大(到底有多么庞大算庞大,这个需要测试,因为每一个硬件环境不同)
条件2:该字段经常出现在where的后面,以条件的形式存在,也就是说这个字段总是被扫描。
条件3:该字段很少的DML(insert delete update)操作。(因为DML之后,索引需要重新排序。)
建议不要随意添加索引,因为索引也是需要维护的,太多的话反而会降低系统的性能。
建议通过主键查询,建议通过unique约束的字段进行查询,效率是比较高的。
索引的创建与删除
- 创建索引:create index 索引名 on 表名(字段名);
给emp表的ename字段添加索引,起名:emp_ename_index - 删除索引:rop index emp_ename_index on emp;
将emp表上的emp_ename_index索引对象删除。 - 查看一个SQL语句是否使用了索引进行检索:explain select * from emp where ename = ‘KING’;
索引失效
- select * from emp where ename like ‘%T’;原因是因为模糊匹配当中以“%”开头了,尽量避免模糊查询的时候以“%”开始。
- 使用or的时候会失效,如果使用or那么要求or两边的条件字段都要有索引,才会走索引,如果其中一边有一个字段没有索引,那么另一个字段上的索引也会实现。
- 使用复合索引的时候,没有使用左侧的列查找,索引失效。复合索引是两个字段,或者更多的字段联合起来添加一个索引,叫做复合索引。
- 在where当中索引列参加了运算,索引失效。
- 在where当中索引列使用了函数。
索引分类
单一索引:一个字段上添加索引。
复合索引:两个字段或者更多的字段上添加索引。
主键索引:主键上添加索引。
唯一性索引:具有unique约束的字段上添加索引。
注意:唯一性比较弱的字段上添加索引用处不大。
DDL数据定义语言
- 凡是带有create、drop、alter的,主要操作的是表的结构,不是表中的数据;
表的创建
create table 表名(字段名1 数据类型,字段名2 数据类型,…,字段名n 数据类型);
表名:建议以t_ 或者 tbl_开始,可读性强。见名知意。
字段名:见名知意。
表名和字段名都属于标识符。
数据类型后面可以加default指定默认值,如 char default ‘male’,
数据类型:
- varchar(最长255)
可变长度的字符串
比较智能,节省空间。
会根据实际的数据长度动态分配空间。
优点:节省空间
缺点:需要动态分配空间,速度慢。 - char(最长255)
定长字符串
不管实际的数据长度是多少。
分配固定长度的空间去存储数据。
使用不恰当的时候,可能会导致空间的浪费。
优点:不需要动态分配空间,速度快。
缺点:使用不当可能会导致空间的浪费。
varchar和char我们应该怎么选择?
性别字段你选什么?因为性别是固定长度的字符串,所以选择char。
姓名字段你选什么?每一个人的名字长度不同,所以选择varchar。
- int(最长11)
数字中的整数型。等同于java的int。 - bigint
数字中的长整型。等同于java中的long。 - float
单精度浮点型数据 - double
双精度浮点型数据 - date
短日期类型 - datetime
长日期类型 - clob
字符大对象
最多可以存储4G的字符串。
比如:存储一篇文章,存储一个说明。
超过255个字符的都要采用CLOB字符大对象来存储。
Character Large OBject:CLOB - blob
二进制大对象
Binary Large OBject
专门用来存储图片、声音、视频等流媒体数据。
往BLOB类型的字段上插入数据的时候,例如插入一个图片、视频等,
你需要使用IO流才行。
快速创建表
create table emp2 as select * from emp;
原理:将一个查询结果当做一张表新建,这个可以完成表的快速复制,表创建出来,同时表中的数据也存在了!!!
约束
在创建表语句的字段数据类型后面添加约束,约束直接添加到列后面的,叫做列级约束。
unique(字段1,字段2) // 约束没有添加在列的后面,这种约束被称为表级约束。需要给多个字段联合起来添加某一个约束的时候,需要使用表级约束。
联合约束,直接将多种约束添加在数据类型后面;
-
非空约束:not null
只有列级约束,没有表级约束 -
唯一性约束: unique
字段虽然被unique约束了,但是可以为NULL -
主键约束: primary key (简称PK)非常重要
主键约束:就是一种约束。
主键字段:该字段上添加了主键约束,这样的字段叫做:主键字段
主键值:主键字段中的每一个值都叫做:主键值。
主键值是每一行记录的唯一标识,是每一行记录的身份证号。!
任何一张表都应该有主键,没有主键,表无效!!
主键的特征:not null + unique(主键值不能是NULL,同时也不能重复!)
create 表名(
主键字段名 数据类型 primary key, //列级约束
字段名 数据类型
);
// 一张表,主键约束只能添加1个。(主键只能有1个。)
create table t_vip(
字段名1 数据类型,
字段名2 数据类型,
primary key(主键字段名) // 表级约束,可以多个字段作为联合主键(不建议使用联合主键)
);
//在mysql当中,有一种机制,可以帮助我们自动维护一个主键值
create table t_vip(
id int primary key auto_increment, //auto_increment表示自增,从1开始,以1递增!
name varchar(255)
);
主键值建议使用:int、bigint、char等类型。不建议使用:varchar来做主键。主键值一般都是数字,一般都是定长的!
主键除了:单一主键和复合主键之外,还可以这样进行分类:自然主键:主键值是一个自然数,和业务没关系;业务主键:主键值和业务紧密关联,例如拿银行卡账号做主键值。这就是业务主键!
- 外键约束:foreign key(简称FK)非常重要
外键约束:一种约束(foreign key)
外键字段:该字段上添加了外键约束
外键值:外键字段当中的每一个值。
用了外键约束之后,表之间存在父子关系,被引用的表为父表。删除时,先子后父;创建时,先父后子;插入时,先父后子;
约束写在子表创建字段后面,foreign key(子表中要关联的外键字段) reference 父表(被关联的外键字段)
子表中的外键引用的父表中的某个字段,被引用的这个字段必须是主键吗?不一定是主键,但至少具有unique约束。
外键值可以为NULL。
检查约束:check(mysql不支持,oracle支持)
TCL事务控制语言
DCL数据控制语言
其他
- xxxx.sql这种文件被称为sql脚本文件。
sql脚本文件中编写了大量的sql语句。
我们执行sql脚本文件的时候,该文件中所有的sql语句会全部执行!
批量的执行SQL语句,可以使用sql脚本文件。
在mysql当中怎么执行sql脚本呢?
mysql> source D:\course\03-MySQL\document\vip.sql
存储引擎
存储引擎是MySQL中特有的一个术语,其它数据库中没有。(Oracle中有,但是不叫这个名字)
实际上存储引擎是一个表存储/组织数据的方式。
不同的存储引擎,表存储数据的方式不同。
- 给表添加/指定“存储引擎”:建表时指定存储引擎,以及字符编码方式。
create table t_product(
id int primary key,
name varchar(255)
)engine=InnoDB default charset=gbk;
mysql默认的存储引擎是:InnoDB
mysql默认的字符编码方式是:utf8
查看mysql支持哪些存储引擎:show engines \G;
mysql常用的存储引擎:
-
MyISAM存储引擎?
它管理的表具有以下特征:
使用三个文件表示每个表:
1.格式文件 — 存储表结构的定义(mytable.frm)
2.数据文件 — 存储表行的内容(mytable.MYD)
3.索引文件 — 存储表上索引(mytable.MYI):索引是一本书的目录,缩小扫描范围,提高查询效率的一种机制。
可被转换为压缩、只读表来节省空间
对于一张表来说,只要是主键,
或者加有unique约束的字段上会自动创建索引。
特点:可被转换为压缩、只读表来节省空间,这是这种存储引擎的优势!
不支持事务机制,安全性低。 -
InnoDB存储引擎?
这是mysql默认的存储引擎,同时也是一个重量级的存储引擎。
InnoDB支持事务,支持数据库崩溃后自动恢复机制。
InnoDB存储引擎最主要的特点是:非常安全。
它管理的表具有下列主要特征:
1.每个 InnoDB 表在数据库目录中以.frm 格式文件表示
2.InnoDB 表空间 tablespace 被用于存储表的内容(表空间是一个逻辑名称。表空间存储数据+索引。)
3.提供一组用来记录事务性活动的日志文件
4.用 COMMIT(提交)、SAVEPOINT 及ROLLBACK(回滚)支持事务处理
5.提供全 ACID 兼容
6.在 MySQL 服务器崩溃后提供自动恢复
7.多版本(MVCC)和行级锁定
8.支持外键及引用的完整性,包括级联删除和更新
最大的特点就是支持事务:以保证数据的安全。
缺点:效率不是很高,并且也不能压缩,不能转换为只读,不能很好的节省存储空间。 -
MEMORY存储引擎
使用 MEMORY 存储引擎的表,其数据存储在内存中,且行的长度固定,
这两个特点使得 MEMORY 存储引擎非常快。
MEMORY 存储引擎管理的表具有下列特征:
1.在数据库目录内,每个表均以.frm 格式的文件表示。
2.表数据及索引被存储在内存中。(目的就是快,查询快!)
3.表级锁机制。
4.不能包含 TEXT 或 BLOB 字段。
MEMORY 存储引擎以前被称为HEAP 引擎。
优点:查询效率是最高的。不需要和硬盘交互。
缺点:不安全,关机之后数据消失。因为数据和索引都是在内存当中。
视图
创建视图:create view dept2_view as select * from dept2;
删除视图:drop view dept2_view;
注意:只有DQL语句才能以view的形式创建。create view view_name as 这里的语句必须是DQL语句;
视图的作用:可以面向视图对象进行增删改查,对视图对象的增删改查,会导致原表被操作!(视图的特点:通过对视图的操作,会影响到原表数据。)
//面向视图查询
select * from dept2_view;
// 面向视图插入
insert into dept2_view(deptno,dname,loc) values(60,'SALES', 'BEIJING');
// 查询原表数据
select * from dept2;
// 面向视图删除
delete from dept2_view;
// 查询原表数据
select * from dept2;
// 创建视图对象
create view
emp_dept_view
as
select
e.ename,e.sal,d.dname
from
emp e
join
dept d
on
e.deptno = d.deptno;
// 查询视图对象
select * from emp_dept_view;
C:Create(增)
R:Retrive(查:检索)
U:Update(改)
D:Delete(删)
DBA常用命令
- 数据导出
注意:在windows的dos命令窗口中:
mysqldump bjpowernode>D:\bjpowernode.sql -uroot -p123456
导出指定的表:
mysqldump bjpowernode emp>D:\bjpowernode.sql -uroot -p123456 - 数据导入?
注意:需要先登录到mysql数据库服务器上。
然后创建数据库:create database bjpowernode;
使用数据库:use bjpowernode
然后初始化数据库:source D:\bjpowernode.sql
数据库设计三范式
数据库设计范式:数据库表的设计依据。教你怎么进行数据库表的设计。
- 第一范式:要求任何一张表必须有主键,每一个字段原子性不可再分。
最核心,最重要的范式,所有表的设计都需要满足。 - 第二范式:建立在第一范式的基础之上,要求所有非主键字段完全依赖主键,不要产生部分依赖。
- 第三范式:建立在第二范式的基础之上,要求所有非主键字段直接依赖主键,不要产生传递依赖。
一对多:一对多,两张表,多的表加外键
多对多:多对多,三张表,关系表两个外键
因为在sql当中,表和表之间连接次数越多,效率越低。(笛卡尔积)
有的时候可能会存在冗余,但是为了减少表的连接次数,这样做也是合理的,
并且对于开发人员来说,sql语句的编写难度也会降低。