SQL:力学笃行篇

不知道各位看官,是否有过想尝试设计表的想法呢?
数据间纷乱复杂的关系,又该如何下手呢?
满足什么样的设计原则,才可以符合正确、完整、一致且安全的要求呢?
或许,小采风想和各位一起学习一下表的设计规范。子曾经曰过:欲穷天下之事,深谙套路二字。踏着深深的套路,走进今天的力学笃行

一、表的设计步骤

  • 需求分析:存储 处理 安全 完整

有位同学,专职于后台产品经理,负责产品具体的业务逻辑,有幸有过一次深聊。业务逻辑,需要正确的框架和允许范围的波动。人算不如天算,充分考虑产品的存储需求、数据处理需求、数据的安全需求和完整性要求,这是文档要求,我们这些小白能做的,莫过于尽力符合文档需求,往往不是一蹴而就的。

  • 逻辑设计:结构 关系 冗余 异常

什么样的表设计,是足够合理呢?符合三大范式设计原则的表,至少是正确完整的,但是往往不是足够高效的,需要进行一定的反范式化设计。
1 第一范式(不再具体介绍)
字段只有单一属性且不可分割,由基本数据类型组成,必须是简单的二维表;
2 第二范式
不能允许非主键列对部分主键存在依赖关系;
3 第三范式
既不部分依赖于业务主键,也不传递依赖于业务主键;

看完三大范式,不要急着骂小采风。这不就是天书吗?完全没有办法去理解,我们还是看一看具体的例子,尝试搞清楚这些苦涩的范式吧!

//从选课表中理解第二范式
create table select_course1(
   stu_id int(10) comment '学号',
   stu_name varchar(20) comment '姓名',
   course_name varchar(10) comment '课程名称',
   course_credit int(2)  comment '学分',
   //学生和课程作为主键,唯一标识
   primary key(stu_id,course_name)
)engine=Innodb;

简单一看,这个表的设计,是没有问题的。可是,如果有一天高数从6个学分变成8个学分了,我们还需要一步步更改表中每行记录,完成高数学分的修改吗?问题的本质是,非主键course_credit列对复合主键列(stu_id,course_name)中的course_name存在部分依赖,即高数学分依赖于高数课程,违反第二范式会产生插入、更新和删除异常,应该将其更改为两张表,具体如下:

//更改为选择表和课程表
create table select_course2(
   stu_id int(10) comment '学号',
   stu_name varchar(20) comment '姓名',
   course_name varchar(10) comment '课程名称',
   //学生和课程作为主键,唯一标识
   primary key(stu_id,course_name)
)engine=Innodb;
create table course(
   course_name varchar(10) comment '课程名称',
   course_credit int(2) comment '学分',
   primary key(course_name)
)engine=Innodb;

那么存在一个问题,如何生成一张和select_course1相同的表呢?可以使用关联(join)将select_course2和course连接生成;

稍事休息,一起来看一看第三范式的理解:

//学生信息表
create table stu_info(
   stu_id int(10) comment '学号',
   stu_name varchar(20) comment '姓名',
   school_name varchar(20) comment '学院名称',
   school_tel varchar(20) comment '学院电话',
   primary key(stu_id)
)engine=Innodb;

这张表的问题在于,从学号可以知道学院名称,从学院名称可以知道学院电话,本质是,school_tel部分依赖于stu_id,即学院电话部分依赖于学院学号,违反第三范式,应该将其拆分成两张表,具体如下:

create table stu_info(
   stu_id int(10) comment '学号',
   stu_name varchar(20) comment '姓名',
   school_name varchar(20) comment '学院名称',
   primary key(stu_id)
)engine=Innodb;
create table school_info(
   school_name varchar(20) comment '学院名称',
   school_tel varchar(20) comment '学院电话',
   primary key(school_name)
)engine=Innodb;

关于三大范式的理解,先暂时告一段落,不然小采风已经迷乱。

  • 物理设计:规范 存储引擎 数据类型 结构

物理设计主要考虑一下问题:

1 定义数据库、表及字段的命名规范
2 选择合适的存储引擎
3 为字段选择合适的数据类型
4 数据库结构
  • 维护优化:SQL优化 索引优化

个人认为,相比于前面三点,这点是最为重要,像是终极大boss一样,需求分析会存在漏洞,逻辑设计会有不足之处,物理设计会有bug,但是维护优化环节,需要更多的经验和更细致的考量。近日,了解了些数据库架构中的内容,主从拓扑,主从切换,MMM和MHA等知识,主库的读写分离,从库的读负载均衡,主从切换时的异步延迟,从库IO线程读取binlog日志的不同,binlog日志bindump命令的阻塞等,面对这样历史大难题,小采风只能知难而退了,留给看官中的您,让世界更美好了。

本文似乎有点头重脚轻,说是力学笃行篇,却并没有一点干货呢?看官们不要着急,下面让我们从现实出发,看看现实场景中,一个业务的表是如何设计的(不再进行具体的表创建等),尾随套路的步伐,一步步登上封顶。

二、需求分析

  • 产品需求
    实现图书类的电商网站
  • 具备功能
    用户登录、用户管理、商品展示、商品管理、在线销售
  • 以电商网站为例考虑

三、逻辑设计

  • 用户登录及管理
//用户信息表:用户名为主键
用户名,密码,手机号,姓名,出生日期,在线状态
//具体分析
1 以用户名为主键,满足第二范式设计;
2 不存在部分依赖和传递依赖关系,符合第三范式设计
  • 商品展示及管理
//商品信息表:(商品名称,分类名称)联合主键
商品名称,分类名称,出版社名称,图书价格,
图书描述,作者
//具体分析
1 图书描述对商品名称存在依赖关系;
2 仅有分类名称,没有分类描述,完整性不足;

可以考虑将商品信息拆分成三张表,如下:

//商品信息表:商品名称为主键
商品名称,出版社名称,图书价格,图书描述,作者
//分类信息表:分类名称为主键
分类名称,分类描述
//商品分类关系表:(商品名称,分类名称)联合主键
商品名称,分类名称
  • 订单管理
//订单表:订单编号为主键
订单编号,下单用户,下单日期,订单金额,
订单商品分类,订单商品名,订单商品单价,
订单商品数量,支付金额
//具体分析
1、订单商品分类,订单商品名,订单商品单价已经
存在于商品及分类表中,数据严重冗余;
2、订单金额可以有订单计算而来;

考虑将订单表分为下面两张表,如下:

//订单表:订单编号为主键
订单编号,下单用户名,下单日期,支付金额
//订单商品关联表:(编号,分类,商品名)联合主键
订单编号,订单商品分类,订单商品名,商品数量

以上设计,当然不是最合理的,不过基本满足三大范式的要求,基本实现正确性、完整性和一致性,但是这样的文档式设计,能实现高效查询吗?

//查询某位用户的订单总金额
select 下单用户名,sum(d.商品价格*b.商品数量)
from 订单表 a join 订单商品关联表 b on a.订单编号=b.订单编号
join 商品分类关系表 c on c.商品名称=b.商品名称 
and c.分类名称=b.分类名称
join 商品信息表 d on d.商品名称=c.商品名称
group by 下单用户名
//具体分析
一条简单的查询,需要关联四张表,这样会严重影响查询效率

教科书中,往往并不是最理想的情况,仍然需要对表进行适当的修改:

  • 反范式化设计:增加冗余,减少表的关联
//1 在订单商品关联表中增加商品价格字段,
减少关联商品信息表
订单编号,订单商品分类,订单商品名,
商品数量,商品价格
//2 去除商品分类信息表,
直接基于商品信息表和分类信息表查询
//查询某位用户的订单总金额
select 下单用户名,sum(b.订单商品名*b.商品数量)
from 订单表 a join 订单商品关联表 b
on a.订单编号=b.订单编号
group by 下单用户名

世界总是这般公平,反范式化通过增加冗余,提高查询效率,但是同时增加表结构的冗余和数据维护异常的难度,所以,适当情境下自由适当权衡。

四、物理设计

  • 存储引擎选择
    不同的场景需要不同的存储引擎选择,主要是以下几个方面:
    事务支持,表锁行锁,读写性能,索引优化,主键外键
  • 字段数据类型选择
    1 选择原则
    优先考虑数字类型,其次是日期或二进制类型,最后是字符类型;
    相同级别的数据类型,优先选择占空间小的数据类型;
    2 主键选择
    主键应尽可能小,因为主键索引中包含非主键属性;
    主键应该是顺序增长的,提高插入和删除效率;
    3 VARCHAR和CHAR类型:以字符为单位,不是以字节为单位
    VARCHAR:
    字符串列的最大长度比平均长度大,发挥变长字符特性;
    字符串序列少被更新,减少存储碎片;
    CHAR:
    字符串序列存储长度近似的值;
    适合存储短字符串;
    适合存储更新频度比较高的字符串;
    4 整数类型和实数类型
    整数类型的位数,由实际场景选择;
    float和double类型的数据精度问题,在财务类应用中需要注意;
    5 日期类型:推荐使用date
    字符串存储8字节,datetime存储4字节,int存储4字节,date类型3字节;
    date类型有很多日期时间函数可以使用;

纸上得来终觉浅,觉知此事要躬行。昨天舍友参加了阿里中间件技术的介绍,主讲人一句话,如果说淘宝技术是喜马拉雅山脉 ,那么中间件技术就是珠穆朗玛峰。在mysql的架构中,中间件用于洪水猛兽般的高并发请求时的读写分离和读负载均衡,这是技术造福于社会的终极智慧,向工程师看齐。好久没有这样的长文了,四个字,与君同行。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容