但是我们平常的日常语言,还是会混淆的称呼,下面是严格的对应关系:
关系(relation) 表(table)元组(tuple) 行(row)或记录(record)
势(cardinality) 行数(number of rows)
属性(attribute) 列(column)或字段(field)
度(degree) 列数(number of columns)
定义域(domain) 列的取值集合(pool of legal values)
(4)关系的性质
关系不只是集合,它还有许多非常有趣的性质。
其中之一就是“封闭性”(closure property)。这个性质简单地说就是“运算的输入和输出都是关系”,换句话来说,就是“保证关系世界永远封闭”的性质。
2、范式(NF) (1)诞生上面提到关系模型诞生历史,即 Codd 在 1970 年的第二篇论文里,首次出现了范式的概念,不过只有第一范式的想法(第二范式、第三范式的定义陆续出现在他之后的论文中)。
(2)什么是范式?范式是“符合某一种级别的关系模式的集合,表示一个关系内部各属性之间的联系的合理化程度”。通俗理解就是:一张数据表的表结构所符合的某种设计标准的级别。
目前关系数据库有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,又称完美范式)。
满足最低要求的范式是第一范式(1NF)。在第一范式的基础上进一步满足更多规范要求的称为第二范式(2NF),其余范式以次类推。一般说来,数据库只需满足到第三范式 (3NF)就行了。
(3)前三个范式1、1NF:列是最小的单元(原子性约束),不可再分。
如:一个”省市区“字段,同时存了省市区(可能是用逗号分隔的字符串类型、或者是用了数组类型),应该拆分成"省"、"市"和“区”。
一般来说,在宿主语言中可以灵活选择数组、结构体、对象等多种数据类型来表现非规范化的数据。但是在插入到数据库中时,必须将它们分解成标量值,即按照第一范式进行规范化,然后再存入数据库。
但在关系数据库诞生三十年后,SQL-99 进行了扩展,使得我们可以定义不满足第一范式的“数组类型”。(还有后来的 JSON/JSONB 类型)这个扩展对关系模型来说究竟是好还是坏,还不能轻易下判断。
然而在宿主语言和数据库之间传递和接收数据时,应该有很多读者因为双方支持的数据结构不一致而苦恼过吧?特别是面向对象语言和关系数据库不一致的问题,这种问题称为“阻抗不匹配”。由此可见,希望数据库能支持在宿主语言中可用的数据结构这种需求也是有道理的。
2、2NF:满足1NF。表中要有主键(惟一性约束),且非主键列必须完全依赖于全部主键而非部分主键。
如:一个订单表【OrderDetail】(OrderID,ProductID,ProductName,ProductUnitPrice,Quantity),OrderID + ProductID 是主键,虽然 Quantity 是完全依赖于 OrderID + ProductID 主键的,但 ProductName、ProductUnitPrice 只部分依赖于 ProductID 主键,所以应该把 OrderDetail 表拆分为 Order 表 和 Product 表。
3、3NF:满足2NF。非主键列是直接依赖于主键,而不是直接依赖于非主键列。
如:还是上面的例子, 一个订单表【OrderDetail】(OrderID,ProductID,ProductName,ProductUnitPrice,Quantity),仅 OrderID 是主键,ProductID,ProductName,ProductUnitPrice,Quantity 都确实完全依赖 OrderID 主键,但其中,ProductName,ProductUnitPrice 是通过先依赖 ProductID,再通过 ProductID 依赖 OrderID 的方式来传递依赖的。所以还是应该把 OrderDetail 表拆分为 Order 表 和 Product 表。
(4)范式的利弊因为范式的主要目的是为了消除冗余(范式级别越高,冗余越小),所以:
好处:
降低存储成本(数据库范式是在20世纪提出的,当时的磁盘存储成本还很高。)
提高拓展性
坏处:
降低性能。没有任何冗余的表设计会产生更多的查询行为。
增加设计表结构的难度
(5)反范式设计既然范式是为了消除冗余,那么反范式就是通过增加冗余、聚合的手段来提升性能。尤其对现在的互联网应用来说,性能比存储成本的要求更高。
参考最近又流行的 noSQL 数据库,就是大大的冗余。
建议:还是根据自身的业务特点在范式和反范式中找到平衡点。
十二、进阶 - 性能优化 1、尽量避免排序与编程(面向过程的)语言不同,在 SQL 语言中,用户不能显式地命令数据库进行排序操作。
所以,能触发排序的代表性的运算有下面这些:
GROUP BY 子句
ORDER BY 子句
聚合函数(SUM、COUNT、AVG、MAX、MIN)
DISTINCT
集合运算符(UNION、INTERSECT、EXCEPT)没加 ALL
窗口函数(RANK、ROW_NUMBER 等)
为了性能,请尽量避免触发排序,如果不能,也尽量针对索引字段的排序。
2、没有用到索引的几种情况 (1)在索引字段上进行运算使用索引时,条件表达式的左侧应该是原始字段。
错误:WHERE col_1 * 1.1 > 100; 正确:WHERE col_1 > 100 / 1.1 (2)使用 IS NULL / IS NOT NULL 谓词因为 NULL 并不是值 ,所以索引字段并不会索引 NULL。
此处存疑,可见这篇辩驳:https://juejin.im/post/5d5defc2518825591523a1db
(3)使用否定形式例如:
<>
NOT IN
NOT EXISTS
(4)使用联合索引时,列的顺序错误