源自:Stairway to Database Design Level 3: Building Tables

作者Joe Celko

翻译:刘琼滨 谢雪妮 许雅莉 赖慧芳

译文:

对于设计和创建数据库完全是个新手?没关系,Joe Celko, 世界上读者数量最多的SQL作者之一,会告诉你这些基础。和往常一样,即使是最专业的数据库老手,也会给他们带来惊喜。Joe是DMBS杂志是多年来最受 读者喜爱的作者。他在美国、英国,北欧,南美及非洲传授SQL知识。他在ANSI / ISO SQL标准委员会工作了10年,为SQL-89和SQL-92标准做出了杰出贡献。

有很多类型的表,每个表都有它们特定规则和完整性约束的要求。无论需求是什么,表层级的约束会确保被执行,维护数据的完整性。

在表里,列只会出现一次。这样做是有道理的;如果你两次记录某人的鞋子大小,这将是多余的,当列不一致时是混淆的。现在我们可以有表层级的在每行的列里的检查(CHECK)约束。这和之前列上的(CHECK)检查并没有啥区别。它们可以在CREATE TABLE语句里,多个列声明里命名并出现,不附加到任何行。例如:

CONSTRAINT Valid_Employee_Age-- don't hire people before they are born

CHECK (emp_birth_date < emp_hire_date)

一般不把检查组合成一个大的CHECK()子句,错误信息会包含约束名称,因此独立的约束相比单个复杂命名的约束,能让你更清楚的发现问题。

接下来是我们的冗余问题,在表层级我们想每个行因同个原因而唯一,这可以通过约束实现。两个表层级的约束是UNIQUE和PRIMARY KEY,它们可以是单列或多列组合。

UNIQUE约束表示在表里,列或列的组合是唯一的。但在列或多个列中有NULL,如果它是唯一值,我们还是允许的。PRIMARY KEY声明,与对于表里面的所有列,NOT NULL且UNIQUE有同样的效果,但按照惯例,一个表只能有一个PRIMARY KEY声明,这些列用来作为表之间的其他约束,但现在不在乎这些。

唯一性约束如何使用取决于表的类型,一般来说,我们可以将一个表分成三种类型:

1.实体(entity)

2.关系(relationship)

3.辅助(auxiliary)

实体表是一组相同类型的元素的集合,它们由列所建模的属性定义。每一行都是这类元素的一个实例,每一行都有相同的列。如果你能看到它的感觉,看到或触摸它,那它就是一个实体。实体表的名称不应该是单数(除非真的这个集合中只有一个成员),因为它为一个集合建模,命名应该为复数,可以的话,则用集合命名。例如,“Employee”不好,“Employees”更好,“Personnel”最好。“Tree”不好,“Trees”更好,“Forest”最好。你可以添加你自己的示例。

实体也分弱强,一个强大的实体存在有它自身的优点,而一个脆弱的实体存在于一个或多个强大的实体中。就比如你需要购买才能打折。

关系表指的是一个或多个实体表,并且它们之间建立关系。关系可以有它自己委外引用实体的属性。结婚登记号属于婚姻,而不属于丈夫,妻子或牧师。

关系级别是关系里实体的个数,二元关系有2个实体,在实际应用中我们喜欢它们,因为它们简单;二元迭代关系关联到实体本身,一般的n元关系涉及n个实体,就像有买家,卖家和银行的房贷。通常不能把n元关系分解为二元关系。成员的关系可以是可选或必须的。可选的关系表示我们可以有一类的0实体——并不是所有的买卖都有折扣。

关系基数是对于每2个实体,相关出现的实际数量。关系的基本连接类型有:1:1,1:n,和n:n。这些术语通常是符合可选(0或更多)或必须的(1或更多)的关系。

1:1关系是一个实体A的最多一个实例与实体B的一个实例关联的时候。例如,拿通常的丈夫和妻子的关系。每个丈夫有且只要一个妻子;每个妻子有且只有一个丈夫。在这个例子都是必须一个的。

1:n关系是实体A的一个实例,对于实体B的一个实例有0个,一个或多个实体B的实例,实体A的实例只有一个的时候。一个例子会是一个部门有很多员工;每个员工分配到一个部门。取决于你的业务规则,你会允许未分配部门的员工或空的部门。

n:n关系,有时称作非特定的,对于实体A的一个实例,有0个,一个或多个实体B的实例,并且对于实体B的一个实例,有0个,一个或多个实体A的实例。这样的例子可以是披萨和客户。

辅助表不是实体也不是关系;它提供信息。它们是像日历或在SQL里替换计算的查询表(look up tables)。它们经常被误解被当实体或关系表对待。

我们来具体说下。销售订单是客户(实体)和我们的库存(实体)之间的关系。订单明细是存在的弱实体,因为我们有订单。这个关系有一个不是库存或客户一部分的订单号。运费从辅助表获得。对于这个例子,这里我用了一些骨架表。对于订单项目,我使用GTIN(Global Trade Item Number),对于客户,我使用GUNS(Data Universal Numbering System)。在你设计数据库的时候,都要记得先看看行业标准。

CREATE TABLE Sales_Orders

(order_nbr INTEGER NOT NULL PRIMARY KEY

CHECK (order_nbr > 0),

customer_duns CHAR(9) NOT NULL,

order_shipping_amt DECIMAL (5,2) NOT NULL

CHECK (shipping_amt >= 0.00),

etc);

CREATE TABLE Sales_Order_Details

(order_nbr INTEGER NOT NULL,

gtin CHAR(15) NOT NULL,

PRIMARY KEY (order_nbr, gtin),

item_qty INTEGER NOT NULL

CHECK (item_qty > 0),

item_unit_price DECIMAL (8,2) NOT NULL

CHECK (item_unit_price >=0.00));

CREATE TABLE Customers

(customer_duns CHAR(9) NOT NULL PRIMARY KEY

CHECK (customer_duns LIKE '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]'),

etc);

CREATE TABLE Inventory

(gtin CHAR(15) NOT NULL PRIMARY KEY

CHECK (gtin LIKE '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]'),

onhand_qty INTEGER NOT NULL

CHECK (onhand_qty >= 0),

我们可以看到订单表是客户和库存间的关系。订单有它们自己的主键(order_nbr),但没有东西强制我们使用有效的客户DUNS号或对于我们库存里的产品GTIN号。事实上,我可以插入显然无效的DUNS和GTIN码到订单表,现在就这样声明。

这就是我们要引入REFERENCES子句的地方。它是让我们从数据模型强制所有基数和程度的东西。引用(reference)不是个链接或指针。这些是物理概念,引用是个逻辑概念,我们不知道它如何实现。它强制的是,在引用表里,引用表列符合单行的规则。这意味着在引用表里的行必须唯一;默认情况下,在引用表里可以使用主键(PRIMARY KEY),但不必这样。在引用表的值可以称为外键(Foreign Keys)——它们不在它们的表里,但在架构里的其它地方。

这是上面有更多信息的主要架构:

CREATE TABLE Sales_Orders

(order_nbr INTEGER NOT NULL PRIMARY KEY

CHECK (order_nbr > 0),

customer_duns CHAR(9) NOT NULL

REFERENCES Customers(customer_duns),

order_shipping_amt DECIMAL (5,2) DEFAULT 0.00 NOT NULL

CHECK (shipping_amt >= 0.00),

etc);

CREATE TABLE Sales_Order_Details

(order_nbr INTEGER NOT NULL

REFERENCES Orders(order_nbr),

gtin CHAR(15) NOT NULL

REFERENCES Inventory(gtin),

PRIMARY KEY (order_nbr, gtin),-- two column key

item_qty INTEGER NOT NULL

CHECK (item_qty > 0),

item_unit_price DECIMAL (8,2) NOT NULL

CHECK (item_unit_price >= 0.00));

CREATE TABLE Customers

(customer_duns CHAR(9) NOT NULL PRIMARY KEY

CHECK (customer_duns LIKE '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]'),

etc);

注意,在DUNS和GTIN是主键的地方,我们只有CHECK()约束,而不是在它们出现的引用表里。实体表,客户和库存都被引用;关系表,订单,引用了其它表。这是一般模式,但不是固定的。

这个子句的多列形式如下:

FOREIGN KEY (order_nbr, gtin)

REFERENCES Sales_Order_Details(order_nbr, gtin)

在FOREIGN KEY子句的列是引用表里需要匹配的引用主键,列对列,但可能会有不同的名称。我可以通过在相应的地方放置唯一约束来设置1:1,1:n和n:n的关系。作为辅助表的一个例子,我们可以根据订单的总额来计算运输成本,这张表看起来像这样:

CREATE TABLE Shipping_Costs

(start_order_amt_tot DECIMAL (10,2) NOT NULL,

end_order_amt_tot DECIMAL (10,2) NOT NULL,

CONSTRAINT Valid_Shipping_Range

CHECK (start_order_amt_tot < end_order_amt_tot),

PRIMARY KEY (start_order_amt_tot, end_order_amt_tot),

shipping_amt DECIMAL (5,2) NOT NULL

CHECK (shipping_amt > 0.00));

虽然我们在辅助运费表上声明了主键(PRIMARY KEY),但它不像实体的主键——没有验证或核查,也不是标识符。使用这个表,我们将以类似查询:

SELECT shipping_amt

FROM Shipping_Costs

WHERE <order amount total> BETWEEN start_order_amt_tot AND end_order_amt_tot;

作为练习,尝试写下会从重复和断层上阻止开始和结束范围的约束。如果你需要的话,可以重新设计表。

在修正后的主要架构里,当你试着对一个没有没有库存的产品下单时,你会收到错误提示“没有库存!”,这样的话,你可以试下别的。但如果你尝试从库存里删除产品,你同样也会收到错误提示“额,有人已经下了此产品的订单”,因此在从库存里删它之前,你必须到每个订单用别的值或NULL值来替换它。

这里就是引用完整性(Declarative Referential Integrity (DRI))用的地方。语法是:

ON DELETE [NO ACTION | SET DEFAULT | SET NULL | CASCADE]

ON UPDATE [NO ACTION | SET DEFAULT | SET NULL | CASCADE]

删除和更新是所谓的“数据基础事件(data base events)”;当它们发生到表时,就会发生DRI操作。

NO ACTION:事务回滚,你收到提示。当有简单的REFERENCES子句时的默认操作。

SET DEFAULT:引用的列通过事件改变,但引用的列值会修改为它们的默认值。当然,引用的列在它们上面要有声明的默认值。这些默认需要在引用表里。

SET NULL:引用的列通过事件改变,但引用的列值会修改为NULL。当然,引用的列允许NULL值。这是引入NULL值“无罪推定(benefit of the doubt)”的地方。

CASCADE:引用的列通过事件改变,这些值会级联到引用的列。实际中这是最重要的选项。例如,如果我们想停止一个产品,我们可以从库存里删除它,ON DELETE CASCADE会让SQL引擎会在Sales_Order_Details自动删除匹配的行。同样,如果在库存里更新一个项目,ON UPDATE CASCADE会用新值自动替换引用的列。

在这些操作完成后,引用完整性约束还是有效的。这是最终的架构:

CREATE TABLE Sales_Orders

(order_nbr INTEGER NOT NULL PRIMARY KEY

CHECK (order_nbr > 0),

customer_duns CHAR(9) NOT NULL

REFERENCES Customers(customer_duns)

ON UPDATE CASCADE

ON DELETE CASCADE,

order_shipping_amt DECIMAL (5,2) DEFAULT 0.00 NOT NULL

CHECK (shipping_amt >= 0.00),

etc);

CREATE TABLE Sales_Order_Details

(order_nbr INTEGER NOT NULL

REFERENCES Orders(order_nbr)

ON UPDATE CASCADE

ON DELETE CASCADE,

gtin CHAR(15) NOT NULL

REFERENCES Inventory(gtin)

ON UPDATE CASCADE

ON DELETE CASCADE,

PRIMARY KEY (order_nbr, gtin),-- two column key

item_qty INTEGER NOT NULL

CHECK (item_qty > 0),

item_unit_price DECIMAL (8,2) NOT NULL

CHECK (item_unit_price >= 0.00));

看看下面的情况发生时,你觉得会发生什么?

1.一个客户走了,我们删除它。

2.我们修改Lawn Gnome雕像为更有品味的Pink Flamingo。

3.我们停止销售Pink Flamingo。

4.在1-3步骤后,有人尝试下Lawn Gnome订单。

显然,我留下未处理的问题和其他东西,但我们会接触这些。

原文链接:

http://www.sqlservercentral.com/articles/Stairway+Series/69927/

转载于:https://www.cnblogs.com/hualalalala/p/7748570.html

数据库设计层次3:构建表相关推荐

  1. saas的计费数据库设计_如何构建和扩展SaaS计费解决方案

    saas的计费数据库设计 您需要的最低可行产品 (What you need for a Minimum Viable Product) When you are building your Soft ...

  2. 大数据量下数据库设计及分库分表拆分原则

    在设计数据库的建设前,首先准备工作是了解业务,然后根据业务数据进行数据库设计,设计的同时需要考虑业务数据量.可改造性.数据弱一致性.读写分离.复杂SQL多样化等: 首先,可数据根据读弱一致性可以考虑添 ...

  3. MySQL数据库设计概念(多表查询事务操作)

    每天进步一点点 数据库设计概念 数据库设计简介 表关系(多对多) 表关系(一对多) 表关系之一对一 多表查询 笛卡尔积现象 内连接查询 外连接查询 嵌套查询(子查询) 事务操作 事务的概念 手动提交事 ...

  4. php空间 数据库设计,php进阶之数据库设计/ 选择合适的表引擎

    什么是表引擎 我们看到的表结构,它的本质是数据在硬盘中的存储.根据不同的特性,数据的存储方式不同.比如:对于每一条数据,在硬盘中它是怎么存储的,怎么压缩的,怎么建立索引和优化的,它的读取和写入是怎么实 ...

  5. python读取erp的数据库_【ERP系统设计】【数据库设计】对数据表重命名和读取建表的SQL语句...

    今天做了一个小Model,就是把另一数据库中的表复制到目标数据库中,但是复制到目标数据库中的表中的记录为空 思路: 1 读取建表的SQL语句 2 通过jdbc执行 3 对新建表进行重新命名 精要: 1 ...

  6. 数据库设计之area区域表

    这个用起来还可以的比较全,是2015年更新的 在这个版本的数据上我做了个小的改动,如果你使用不方便可以参考改版后的数据:http://blog.csdn.net/u012012240/article/ ...

  7. 数据库设计之area区域表改版后,数据库设计之区域表

    原先版本:http://blog.csdn.net/u012012240/article/details/51221080 原先版本直辖市的省级别和市级别为1条记录,1条记录即代表省又代表市,操作起来 ...

  8. 数据库设计(二)——数据库设计原则

    一.数据库表的设计原则 1.不应该针对整个系统进行数据库设计,而应该根据系统架构中的组件划分,针对每个组件所处理的业务进行组件单元的数据库设计:不同组件间所对应的数据库表之间的关联应尽可能减少,如果不 ...

  9. 数据库设计范式实例解析

    设计范式(范式,数据库设计范式,数据库的设计范式)是符合某一种级别的关系模式的集合.构造数据库必须遵循一定的规则.在关系数据库中,这种规则就是范式.关系数据库中的关系必须满足一定的要求,即满足不同的范 ...

最新文章

  1. 12月国内网站流量统计5强:360安全中心后来居上
  2. vmlinux、 Image, zImage、 uImage 的区别
  3. nvidia驱动程序与windows版本不兼容
  4. 业界 | 裁判太嚣张?平昌之后,奥运会评分系统将引入AI技术
  5. 《程序是怎样跑起来的》第一章有感
  6. 524. 通过删除字母匹配到字典里最长单词
  7. Linux kernel之SMP初始化
  8. [设计模式] ------ 抽象工厂模式
  9. javascript 事件冒泡和事件代理
  10. Educational Codeforces Round 54 (Rated for Div. 2): D. Edge Deletion(最短路树)
  11. redis数据结构存储Linked List设计细节(redis的设计与实现笔记)
  12. 基于微服务架构的门户平台改造的研究
  13. mobile 部署和/或注册失败 0x8973190e 解决办法
  14. php 5的手册,本手册中所涉及的 PHP 版本 - PHP 5 中文文档
  15. 黑苹果安装 hackintosh Lenovo Y50-70 TransMac 破解版安装
  16. flutter图片聊天泡泡_Flutter 非常丰富的消息气泡效果合集
  17. 如何将pdf在线翻译成英文?
  18. python---用单个葫芦丝音节合成完整的曲目
  19. [活动预告] Substrate 中的 IBC 跨链模块技术分享 Substrate-ibc
  20. dataView及其用法

热门文章

  1. 可信云最高级认证 百度飞桨企业版BML就是这么飒!
  2. 遥感数据下载——FIRM:VIIRS火点数据、MODIS火点数据简介及下载
  3. FSCapture(录屏,截屏软件)
  4. 怎么把It驻场开发推广出去
  5. 一个电子发烧友的程序员成长之路
  6. Element tree树组件 鼠标双击事件
  7. python cv2什么意思_这次一定要记住opencv和cv2是什么及其基础用法
  8. Python生成个性二维码
  9. python计算题库_python练习题-
  10. 周易六十四卦——需卦