跟着项目学sql(二) 三大范式
序号 | 表名 | 说明 |
1 | permission | 权限表 |
2 | user | 用户表 |
3 | menu | 栏目表 |
4 | news | 新闻表 |
5 | clicks | 浏览信息表 |
ER图如下,并没有创建外键约束,因此本文中所有的外键都是指【逻辑外键】:
我们在数据库表设计时,经常说好的设计最起码要遵循第三范式,那现在用三大范式来检验一下我们的设计
-------------------------------------------------------------------------------------------------------------
数据库三大范式
设计关系数据库时,遵从不同的规范要求,设计出合理的关系型数据库,这些不同的规范要求被称为不同的范式,各种范式呈递次规范,越高的范式数据库冗余越小。
目前关系数据库有8种范式(1NF,2NF,3NF,BCNF,4NF,5NF,DKNF,6NF),每一级范式都是基于前一范式的基础上,例如符合2NF的关系模式,必定符合1NF。一般说来,只需满足第三范式(3NF)就行了。
第一范式:所有属性都是不可分割的原子值
事实上,在目前的DBMS中是不可能拆分属性的。相对于excel来说,mysql的表并没有行合并和列合并的功能,因此从字面意思来看,我们的表很难违反第一范式!
第一范式包括下列指导原则:
1)关系中每个记录的每个属性只能包含一个值; ps:无法做到像Excel那样每个记录包含多个值。
2)关系中的每个记录必须包含相同数量的值; ps:无法做到像Excel那样每个记录包含不同数量的值
3)关系中的每个记录一定不能相同。 ps:要有主键约束或者唯一约束
我们每个表都用非业务主键id列,内容是GUID做主键,符合1NF。
第二范式:在第一范式的基础上,要求非主属性都要和码有完全依赖关系
这句话显然是针对复合主键和联合主键来说的。
1)复合主键:以clicks(id,ip,useragent,news_id,create_time)表为例,我们这里用了非业务主键id列,内容是GUID,避开了2NF。
为了研究2NF,我们删掉id列,再来看这个问题:
表的业务主键是(ip,news_id,create_time),即某ip在某时间浏览了某条新闻,而浏览器(useragent)是完全依赖于业务主键的,这符合2NF。如果我们在表中增加了一个news_title字段,那news_title是只依赖于news_id的,从而违反了2NF。
mysql中不应该使用具有实际意义的column 作为主键,这是因为主键列的值不建议进行修改。第一是因为主键列经常用作其他表的外键,如果某一表的主键被修改了,那牵一发而动全身。第二是考虑到经常更新的列不适合创建索引,这当然也包括mysql中的主键索引了。
这里我们的每个表都用GUID做主键,从而避免了使用复合主键,也避开了这种情况。
2)联合主键:
为了应对表间多对多的关系,需要创建中间表,在NewsDB中,权限表(permission)和用户表(user)就是多对多的关系。
创建中间表
permission_user(permission_id,user_id,create_time)
(permission_id,user_id)就是联合主键,如果这时候在要表中添加username列,那就违反了2NF,
真实的项目中,我们在user表中用了一个permission字段存储用户权限id,把用户的多个权限id用逗号隔开,从而免去了中间表,避免了违反第二范式的可能。这种做法能够成立,主要原因是权限的数量比较固定,而且后台用户数量很小。事实上我们每次对权限表的删除操作,要去更新所有的用户的permission字段,用以去除脏数据,这在用户量多的情况下,是很失败的做法。
比如,我们的新闻表(news)中同样有一个menus字段,用来存储新闻所属的栏目id,多个栏目id用逗号隔开。这里也没有采用中间表,就是很严重的设计问题了!我们应该使用中间表news_menus(id,news_id,menu_id)
除了上面讲的脏数据处理的问题,更主要的还是前台的查询性能问题,相比之下后台的所有功能都应该给它让路。要显示某栏目下的新闻,直接去中间表中查找对应的menu_id,即可筛选出所有的news_id,这变相的起到了索引表的效果。
反观是原来的方式,menus字段里多个栏目id用逗号隔开,我们就只能在where条件中用mysql中的locate()函数了(类似indexOf的功能),这显然是不能接受的。
这里添加中间表news_menus(id,news_id,menu_id)
第三范式:任何非主属性不依赖于其它非主属性。
这显然是针对外键来说的,这里以News表为例:
news(id,title,keywords,...,user_id,username,...state),username依赖于user_id,从而在表面上违反了第三范式。这里多存一个username主要是为了在用户被删除的情况下,还能取到用户名。这关系到后面的报表统计问题和应对可能发生的薪酬纠纷。
如果用户被删除,同时news表里只有一个user_id,那username就无从查起了。也就是说,现在即使把user表中的记录删除,news表中的username仍不受影响,这已经说明news表中的username字段并不依赖于user_id,因此这里并没有违反3NF。
我们还是以上面刚刚添加的新闻菜单表中间表news_menus(id,news_id,menu_id)为例,如果这时候在要表中添加news_title列,则违反了3NF
第二范式和第三范式的区别:
2NF针对复合主键和联合主键,表中属性存在部分依赖
3NF针对外键,在满足2NF的基础上,表中属性依赖于外键,造成传递依赖。
记住:
news_menus(news_id,menu_id,news_title) 违反2NF,部分依赖
news_menus(id,news_id,menu_id,news_title) 违反3NF,传递依赖
一般的在mysql都会用非业务主键,避开了部分依赖,反而造成了传递依赖出现的概率比较大!
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
最后修订的数据库:
序号 | 表名 | 说明 |
1 | permission | 权限表 |
2 | user | 用户表 |
3 | menu | 栏目表 |
4 | news | 新闻表 |
5 | news_menus | 新闻_栏目表 |
6 | clicks | 浏览信息表 |
1、表名:permission(权限表)
序号 | 名称 | 数据类型 | 长度 | 小数位 | 允许空值 | 主键 | 默认值 | 说明 |
1 | id | varchar(45) | 45 | 0 | N | Y | 主键 | |
2 | name | varchar(45) | 45 | 0 | Y | 权限名称 | ||
3 | orderby | int | 10 | 0 | Y | 0 | 排序 | |
4 | create_time | datetime | 23 | 0 | Y | CURRENT_TIMESTAMP | 创建时间 |
2、表名:user(用户表)
序号 | 名称 | 数据类型 | 长度 | 小数位 | 允许空值 | 主键 | 默认值 | 说明 |
1 | id | varchar(45) | 45 | 0 | N | Y | 主键 | |
2 | username | varchar(45) | 45 | 0 | N | 用户名 | ||
3 | password | varchar(45) | 45 | 0 | N | 密码 | ||
4 | permission | varchar(200) | 200 | 0 | Y |
权限id (多个用逗号隔开) |
||
5 | create_time | datetime | 23 | 0 | Y | CURRENT_TIMESTAMP | 创建时间 |
3、表名:menu(栏目表)
序号 | 名称 | 数据类型 | 长度 | 小数位 | 允许空值 | 主键 | 默认值 | 说明 |
1 | id | varchar(45) | 45 | 0 | N | Y | ||
2 | name | varchar(45) | 45 | 0 | N | 栏目名称 | ||
3 | url | varchar(200) | 200 | 0 | N | 栏目url | ||
4 | orderby | int | 10 | 0 | Y | 0 | 排序字段 | |
5 | create_time | datetime | 23 | 0 | Y | CURRENT_TIMESTAMP | 创建时间 |
4、表名:news(新闻表)
序号 | 名称 | 数据类型 | 长度 | 小数位 | 允许空值 | 主键 | 默认值 | 说明 |
1 | id | varchar(45) | 45 | 0 | N | Y | 主键 | |
2 | title | varchar(200) | 200 | 0 | N | 标题 | ||
3 | keywords | varchar(200) | 200 | 0 | Y | 关键词 | ||
4 | menus | varchar(200) | 200 | 0 | N | 所属栏目(逗号隔开) | ||
5 | author | varchar(45) | 45 | 0 | N | 新闻来源 | ||
6 | thumb | varchar(200) | 200 | 0 | Y | 缩略图 | ||
7 | content | text | 65535 | 0 | N | 新闻内容 | ||
8 | user_id | varchar(45) | 45 | 0 | Y | 创建人id | ||
9 | username | varchar(45) | 45 | 0 | Y | 创建人 | ||
10 | create_time | datetime | 23 | 0 | Y | CURRENT_TIMESTAMP | 创建时间 | |
11 | edit_time | datetime | 23 | 0 | Y | 最后编辑时间 | ||
12 | state | tinyint | 1 | 0 | N | 0 |
新闻状态 (0未发布1已发布) |
5、表名:news_menus(新闻_栏目表)
序号 | 名称 | 数据类型 | 长度 | 小数位 | 允许空值 | 主键 | 默认值 | 说明 |
1 | id | varchar(45) | 45 | 0 | N | Y | ||
2 | news_id | varchar(45) | 45 | 0 | N | |||
3 | menu_id | varchar(45) | 45 | 0 | N |
6、表名:clicks(浏览信息表)
序号 | 名称 | 数据类型 | 长度 | 小数位 | 允许空值 | 主键 | 默认值 | 说明 |
1 | id | varchar(45) | 45 | 0 | N | Y | ||
2 | ip | varchar(45) | 45 | 0 | N | 浏览人ip地址 | ||
3 | useragent | varchar(500) | 500 | 0 | Y | 浏览器useragent | ||
4 | news_id | varchar(45) | 45 | 0 | N | 新闻id | ||
5 | create_time | datetime | 23 | 0 | Y | CURRENT_TIMESTAMP | 创建时间 |
跟着项目学sql(二) 三大范式相关推荐
- 跟着项目学sql——查询语句优化(一)
现阶段,笔者的环境中只有SqlServer和Oracle.所以后面的文章更多的会以SqlServer的背景来做了. 来看这样一张表WorkLink: WorkLink表结构 WorkLink表数据,现 ...
- 跟着项目学设计模式(六):三层架构
前面用5个章节介绍了单例模式和工厂系列模式,这个过程中,如果算上网站开发人员的表示层,那么项目经历了二层到多层的演变: 数据访问层+表示层 => 数据访问层+业务逻辑层+Client层+表示层 ...
- MySQL→数据库、启动连接数据库、SQL→DDL数据定义语言及数据类型、DML数据操作语言、DQL数据查询语言、数据库约束→主键、唯一、非空、默认、外键、SQL、三大范式及一多关系、视图、内外连接
MySQL连接退出命令 mysql –uroot -proot mysql –h127.0.0.1 –uroot -proot mysql --host=localhost --user=root - ...
- 【跟着项目学CSS】第一期-闪动LOGO
最近期末比较忙,没有上CSDN,没有回大家私信[非常对不起]. 进阶版JavaScript下周更新. 今天先浅浅学习一下CSS样式. 22 1.body代码 <body><div c ...
- mysql候选关键字_MySQL(三)之SQL语句分类、基本操作、三大范式
一.SQL语句的分类 DML(Data Manipulation Langauge,数据操纵/管理语言) (insert,delete,update,select) DDL(Data Definiti ...
- SQL那些事儿(六)--数据库三大范式
一.基本概念 函数依赖: 通俗描述: 描述一个学生的关系,可以有学号(SNO),姓名(SNAME),系名(SDEPT)等几个属性.由于一个学号只对应一个学生,一个学生只在一个系学习.因此当学号确定之后 ...
- 【狂神】SQL笔记9 --三大范式
规范数据库设计 为什么需要设计 当数据库比较复杂的时候,就需要设计了 糟糕的数据库设计: 数据冗余,浪费空间 数据库插入和删除都会麻烦.异常[屏蔽使使用物理外键] 程序的性能差 良好的数据库设计 节省 ...
- 跟着实例学Go语言(二)
本教程全面涵盖了Go语言基础的各个方面.一共80个例子,每个例子对应一个语言特性点,既适合新人快速上手,也适合工作中遇到问题速查知识点. 教程代码示例来自go by example,文字部分来自本人自 ...
- SQL语句以及三大范式
SQL语句 登陆SQL 首先输入win+r+回车,之后输入mysql -u root -p,回车,之后输入自己的密码. 创建数据库 create database 表名 查看已有数据库 选择数据库 创 ...
最新文章
- 2016年5月9日 晨间日记
- Python函数内置函数
- 集成电路883和883b有什么区别
- 虚拟机添加串口设备方法
- java运行class文件_java命令行下运行class文件
- 5G毫米波通信中一些量化的概念
- 修改oracle数据库内存参数,物理内存扩容,oracle 11g R1数据库相关参数修改
- [jQuery基础] jQuery动效案例(一) -- 弹窗广告、对联广告
- Linux的slab和nginx的区别,nginx中slab机制理解
- LayoutInflater
- C#基础概念 代码样例
- js小例子(标签页)
- html js把json转化为excel,json转换Excel
- android 9坚果r1,坚果R1、小米MIX2S、一加6对比评测 骁龙845旗舰怎么选?
- AltiumDesigner16安装教程
- iphone12 fiddler抓包,代理证书无法下载解决
- 计算机组成原理概念学习DAY3——内部存储器
- Setting up GlusterFS Volumes
- Android设备上一张图片的显示过程
- 南开大学计算机党支部书记,程莉莉
热门文章
- hdu 2602 dp
- 企业发放的奖金根据利润提成。 利润(i)低于或等于10万元时,奖金可提10%; 利润高于10万元,低于20万元时,低于10万元的部分按10%提成,高于10万元的部分,可提成7.5%; 2
- 万一您想单车21,000英里...
- 关于undefined is not an object(evaluating 'HotUpdate.downloadRootDir')错误的解决办法
- 001-Pythonic技术汇总-查看帮助的6种方式与推荐实用度2022年5月3日
- 解决JPA懒加载典型的N+1问题-注解@NamedEntityGraph
- layui数据表格高度调整
- 【Python】念数字
- 清奈市Zoho研发总部工作回忆
- python机器学习第二版(读书笔记)