数据库三范式设计---小白初学+案例引入
背景:
模拟一个简单版计算器,两位操作数运算,运算符号包括+-*/,目前是把这些数据存储到了MySQL数据库中,库为cal,表名为success,如图:
CREATE TABLE success (id INT AUTO_INCREMENT PRIMARY KEY,nums VARCHAR(255) NOT NULL COMMENT '用户输入的运算表达式,如1+1',result DOUBLE NOT NULL COMMENT '运算结果',spend_time BIGINT NOT NULL COMMENT '计算耗时(毫秒)',created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
输入关键字history可以进行查询操作:
问题分析:
- 这种“22+78”的nums存储是非结构化的 ,存储的是表达式字符串
- 还有耗时这一块,应该直接存储7即可,不要存一串描述文字
- 不符合三范式(3NF),表达式和结果之间存在函数依赖(result = function(nums))
- 扩展性差
改进方案:
使用数据库三范式设计,步骤如下:
一、首先先理解三大范式:
-
第一范式(1NF):每个字段都是不可分割的原子值
-
第二范式(2NF):满足1NF,且非主键字段完全依赖主键(针对联合主键)
-
第三范式(3NF):满足2NF,且消除传递依赖(非主键字段间不能有依赖)
二、从需求出发设计:
分析需求:
-
需要记录的有:id,操作数1,运算符,操作数2,结果,耗时,创建时间
-
主实体:
-
计算记录(cal):id、耗时、结果、创建时间
-
-
子实体:
-
操作数(operand):值、位置(即第几个数)
-
运算符(operator):符号类型(比如+-*/)
-
三、表设计:
场景模拟:
现在要保存「3 + 5」的运算记录:
先创建「计算记录」主表条目(生成cal_id=1)
接着要在operands表插入两条记录:
(cal_id=1, position=1, value=3)
(cal_id=1, position=2, value=5)
最后在operators表插入:
(cal_id=1, operator='+')
总结:三表设计方案,分别是cal、operands、operators三张表
四、三表设计(完全范式化)代码:
-- 运算记录主表
CREATE TABLE cal (id INT AUTO_INCREMENT PRIMARY KEY,spend_time BIGINT NOT NULL COMMENT '计算耗时(毫秒)',result DOUBLE NOT NULL COMMENT '计算结果',created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);-- 运算数表
CREATE TABLE operands (id INT AUTO_INCREMENT PRIMARY KEY,cal_id INT NOT NULL COMMENT '关联计算记录',position INT NOT NULL COMMENT '操作数位置(1,2,...)',param DOUBLE NOT NULL COMMENT '操作数值',FOREIGN KEY (cal_id) REFERENCES cal(id)
);-- 运算符表
CREATE TABLE operators (id INT AUTO_INCREMENT PRIMARY KEY,cal_id INT NOT NULL COMMENT '关联计算记录',operator CHAR(2) NOT NULL COMMENT '运算符,如+,-,*,/',FOREIGN KEY (cal_id) REFERENCES cal(id)
);
界面化展示三张表结构:
为什么满足三范式?
-
满足1NF:所有字段都是原子值
-
满足2NF:
-
单列主键不存在部分依赖问题
-
外键关系完整
-
-
满足3NF:
-
消除了传递依赖(如原设计result依赖nums)
-
各表非主键字段只依赖主键
-
但是这种三表设计复杂度比较高(需要多表join查询),但同时灵活性也很高(支持任意数量操作数)
这里我们虽然只是一个简单计算器没必要这么麻烦,但是主要是为了学习三范式数据库表设计,所以还是用三表设计!配合下面的时序图来理解三张表的关系!
修改之后:
注意细节:
cal表的created_at:
下一篇再讲讲三表查询SQL怎么写!^_^