跳转至

关系模型

任何学科的专业人士都需要知道自己所在专业的基础。所以,如果你是数据库领域的专家,就需要知道关系模型,因为它是数据库领域的基础(至少是基础的主体)。

当今,不论是商业化的应用培训还是学术化的理论课程,任何课程都至少提到关系模型的内容,但是大多数教学从结果上看都很糟。

可以确定的是,数据库领域中的大多数人并没有很好地理解关系模型。造成这种情形的可能原因包括:

  • 脱离实践的关系模型教学。就是说,至少对于初学者,理解关系模型知识的关联性或关系模型所要解决的问题是很难的。

  • 授课的教师本身并没有充分理解或意识到关系模型知识内容的重要性。

  • 在实践中更可能存在的问题:就根本没教关系模型,授课中取而代之的是 SQL 语言,或者 SQL 语言的一些方言(比如 Oracle 的 SQL 方言)。

SQL 语言、数据库和关系模型三者经常被混淆。数据库的功能主要是存储数据,这些数据符合对现实世界一部分所建立的特定模型。

相应地,数据库必须提供可靠的基础设施(infrastructure),无论何时都能够让多个用户使用同一些数据,且在数据被修改时不破坏数据 完整性。这要求数据库能够处理来自不同用户的 “ 资源争用(contention ) ” ,并能在事务(transaction)处理过程中遇到机器故障等极端情况下也保持数据一致性。

结构化查询语言(Structured Query Language,SQL)无非是一种语言,虽然它与数据库关系密切。将 SQL 语言和关系数据库等同视之,或者更糟——与关系理论等同视之,都是错误的。这种错误就好比将掌握了电子表软件或文字处理软件视为掌握了 “ 信息技术 ” 。

实际上 ,有些软件产品并非数据库,但它们也支持 SQL

简而言之,关系理论支持我们通过一组关系运算符来搜寻满足某些条件的数据,这些关系运算符几乎支持任何基本查询。

关键在于,关系理论有严格的数学基础,我们完全可以相信同一结果可由不同的关系表达式来获得,正如在算术中 246/369 完全等于 ⅔ 一样。

关系理论只关心如何根据查询条件取得正确的数据集;而对我们这些实践者(而非理论家)而言,关系操作阶段只负责准确无误地找出属于最终数据集的记录,而不同行的相同字段的关系并不是在这个阶段处理,而是完全属于排序操作。

关系理论并不涉及各种统计功能(例如百分位数等),而这些统计功能经常出现在不同的 “ SQL 方言(dialect ) ” 当中。关系理论所研究的是集合(set),但并不涉及如何为这些集合排序。尽管有许多关于排序的数学理论,但它们都与关系理论无关。

当 SQL 引擎处理查询时,会用优化器找出执行查询最高效的方式。此时关系理论又可以大有作为了,优化器借助关系理论,对开发者提供的语义无误的原始查询进行有效的等价变换,即使原始查询编写得相当笨拙。

原理而非产品

“为什么你作为数据库领域专业人员需要懂得关系模型”这个问题是值得花时间去搞清楚的。原因是:关系模型不是特定的产品;相反,它关注于原理。

这里的原理指的是什么?这里有一个定义(来源于《Chambers Twentieth Century Dictionary》)。

"原理:基础的、自然本质的、理论基础的源头,根本,本源:其他课题得以建立或发展的基础事实。"

关于原理的关键在于:原理具有普适性、稳定性。相反,产品和技术(以及 SQL 语言)总是在改变——而原理不变。举例来说,假设你懂 Oracle;事实上,可以假设你是 Oracle 专家。而如果你仅仅懂得 Oracle,那么你的知识就不一定是可移植的,比如 DB2 或 SQL Server 环境(甚至有可能会阻碍你在新环境中的进步)。但是如果你懂得底层原理(也就是说,如果你懂得关系模型),那么你就掌握了可移植的知识和技巧:这些知识和技巧可以应用于任何环境,永不过时。

热衷于实践而不要理论的人好像一个水手登上了一只没有舵和罗盘的船,他拿不准该往哪里航行。实践应以好的理论为基础。

什么是数据库?数据库(database)可以简单的认为是一个数据集合,这些数据互相关联,映射了真实世界的一些数据模型,例如一个班级(class)里的学生(student),学生会选课(student-lesson),老师会上课(teacher-lesson),学生会有考试成绩(student-grade),它们相互关联相互影响。

在数据库的上层,有提供给用户查询数据的语言,最常见的即 SQL(Structured Query Language),这门课程主要专注于如何实现一个功能完备的数据库系统。

1970 年代,数据存储并没有统一的概念和实现,每个应用都需要自己去实现一套数据存储的方案,这非常不利于应用和程序的移植。于是 Ted Codd 在 1970 年提出了关系模型(relational model)的概念,主要有以下三个关键点:

  • 数据存储在简单的数据结构中

  • 能够通过高级语言访问数据库

  • 物理存储的细节交给数据库管理系统实现

数据模型(data model)指的是描述数据库中数据的概念集合,常见的数据模型有以下几种:

  • Relational: 大多数 DBMS 为关系型数据库
  • Key/Value, Graph, Document, Column-family : NoSQL 数据库(列簇数据库如 RocksDB)
  • Array/Matrix: Machine Learning 会使用
  • Hierarchical: 层次模型(不用了)
  • Network: 网状模型(不用了)

其中 relational 是最常见、应用最广泛的关系模型,大多数常见的数据库系统例如 MySQL、PostgreSQL、Oracle 都是关系模型;K/V、Graph、Document、Column Family 属于 NoSQL 类型,例如 rocksdb、neo4j、MongoDB、Redis;Array/Matrix 常用于机器学习领域,例如 TileDB;Hierarchical 和 Network 分别代表层次和网状模型,是很古老的数据模型了,目前已经过时。

关系模型基本逻辑概念

实体

实体是现实世界中可以区分于其他对象的一个事物或对象,每个实体有一组性质,其中一些性质的值可以唯一标识一个实体。 实体是首要的数据对象,常用于表示一个人、地方、某样事物或某个事件。一个特定的实体被称为实体实例(entity instance 或 entity occurrence)。
例如每个人就是一个实体,人有姓名,性别,年龄,身份证等属性。身份证号就可以唯一标识一个人。可以简单把一个实体等价认为一行数据。

 - 实体属性:实体通过一组属性来表示,属性是实体集中的每个实体都拥有描述性信息,比如每个student实体都有 student_id,sex,name,age等属性。<br>
 - 简单属性:不可以进一步拆分的属性,例如:student实体的sex属性只能是male或female。<br>
 - 复合属性:可以进一步拆分的属性,例如:student实体的name属性可以进一步拆分为:first_name和last_name两个子属性。<br>
 - 单值属性:每个特定实体中的某个属性只有一个值。即student表中任何一个行的sex列都只能是一个值,male和female。<br>
 - 多值属性:每个特定实体中的某个属性可能有多个值。即student表中任何一个行的phone_number列可能有多个值,一个学生有多个电话号码。<br>
 - 派生属性:这类属性的值可以从别的相关属性或实体派生出来(也就是可通过别的属性计算出来)。即年龄是通过当前时间和出生日期计算出来的。<br>
实体集

实体集:是相同类型即具有相同属性的实体集合,例如:实体集 student 可以用来标识大学中所有学生的集合。可以简单的把实体集等价认为是一张表。 在数据库建模过程中,

联系

联系:实体与实体之间相互关联,存在关联关系,例如:student 实体与 insructor 实体之间存在关系,表示教师是学生的导师。关系表示一个或多个实体之间的联系。关系依赖于实体,一般没有物理概念上的存在。关系最常用来表示实体之间,一对一,一对多,多对多的对应。关系的构图是一个菱形,关系的名称一般为动词

联系集

联系集:相同类型联系的集合,例如:student 与 insructor 这两个实体集中的多个教师与多个学生之间存在导师-学生关系(advisor)。每个实体集可以参与多个联系集。

 - 联系集的度:参与联系集的实体集的数量称为度,我们常见的是二元联系集,是涉及两个实体的联系集。度为3。<br>

 - 参与:如果实体集中的每个实体都参与到联系集中,则称为完全参与,如果只有部分实体参与到联系中,称为部分参与。<br>
映射基数

映射基数(基数比率):表示一个实体通过联系集可能关联的实体个数。

 - 一对一:A实体集中的一个实体最多与B实体集中一个实体关联,B实体集中的一个实体最多与A实体集中一个实体关联。**每个学生只能有一个老师,每个老师只能有一个学生。**<br>
 - 一对多:A实体集中的一个实体可能与B实体集中任意个实体关联,B实体集中的一个实体最多与A实体集中一个实体关联。**每个学生可以有任意个老师,每个老师只能有一个学生。**<br>
 - 多对一:**多个学生对应一个老师。**<br>
 - 多对多:**多个学生对应多个老师。**<br>

???+ 表 - 在关系数据库中,表也被叫做关系,它是一种行列形式的二维表。其实就是一系列二维数组的集合,用于存储各种信息。
- 关系模型中用 元组 指代 ,用 属性 指代
- 用 关系实例 指代一个关系中的特定实例,也就是一行数据。
- 对于关系中的每个属性的取值范围,称为该属性的域,对于域中的元素,如果是不可再分的单元,则称为为这个域是**原子性**的。

关系模型基础

关系模型是逻辑模型中的一种,也是现在普遍使用的一种。其由实体、属性和关系三者构成。其可以使用一张二维表进行关系呈现。

关系模型(Relational model)由 E.F.Codd 博士于 1970 年提出,以集合论中的关系概念为基础;无论是现实世界中的实体对象还是它们之间的联系都使用关系表示。

我们在数据库系统中看到的关系就是二维表(Table),由行(Row)和列(Column)组成。因此,也可以说关系表是由行构成的集合。

关系模型由 数据结构、数据操作和完整性约束 三部分构成:

  • 构造机制(数据结构):其中数据结构是指数据自身的组织形式,关系模型中的数据结构就是关系表,包括基础表、派生表(查询结果)和虚拟表(视图);
  • 操作机制(数据操作):数据操作是指对数据进行的**增删改查**的各种访问操作,而对数据的查询是该模型中最为重要的操作;
  • 约束机制(数据约束):完整性约束用于维护数据的完整性或者满足业务约束的需求。

作为数据库的一种数据模型,关系模型提供了一组完整性规则或限制。完整性约束用于确定关系数据库里数据的准确性和一致性。

在关系型数据库里,完整性约束包括域完整性、实体完整性、引用完整性以及用户定义完整性。

关系永远也不会包含重复的元组。这一性质是成立的,因为主体的定义是元组的集合,而数学上的集合不包含重复元素。

然而,SQL 不是这样的。一般情况下 SQL 表允许包含重复行。关系的元组是无序的。也是因为数学上的集合是无序的。

关系的属性也没有从左到右的顺序,

数据

数据结构是描述一个数据模型性质最重要的方面。具体来说,它描述了两类内容:

一是数据库对象的类型、内容等(一个模型中有什么样的对象,对象的内容是什么),比如,关系模型有关系,属性,域(属性的取值范围)等对象。

二是数据之间联系有关的对象。

总之,数据结构是对象类型的集合。(表、视图都是对象,他们是不同的类型)

数据操作是在对象的实例上允许进行的操作的集合。比如,在表上允许进行查询、删除等操作。

数据的完整性约束条件是一组规则,这组规则对数据及其关系起到制约的作用。数据模型应该规定这样的一组规则,以保证数据的正确、有效、相容,并提供定义规则的机制。

完整性约束

完整性约束用于维护数据的完整性或者满足业务约束的需求,包括**实体完整性(主键约束)、**参照完整性(外键约束)**以及**用户定义的完整性(非空约束、唯一约束、检查约束和默认值)

数据库的完整性指数据的正确性(correctness)和相容性(compat-ability)。为了维护数据的完整性和一致性,或者为了实现业务需求,SQL 标准定义了完整性约束。

  • 正确性:指数据符合现实语义。
  • 相容性:指同一对象在不同关系表中的数据是符合逻辑的。

为了维护数据库的完整性,数据库管理系统(DBMS)必须实现如下功能:

  • 定义完整性约束 在 SQL 标准中定义了一系列定义完整性约束的语句。

  • 完整性检查 检查数据是否符合完整性约束条件的机制成为完整性检查。完整性检查通常在 INSERT、UPDATE、DELETE 语句执行后开始检查,也可在事务提交时检查。

  • 违约处理 在 DBMS 发现用户的操作违背了完整性约束条件,将采取一定的操作。

关系数据库管理系统使得完整性控制成为其核心支持的功能,从而能为所有用户和应用提供一致的数据库完整性。

域完整性/用户定义完整性

用户定义完整性,有时候也叫域名完整性。

  • 非空约束(NOT NULL),用于确保字段不会出现空值。例如学生信息表中,学生的姓名、出生日期、性别等一定要有数据。
  • 唯一约束(UNIQUE),用于确保字段中的值不会重复。例如每个学生的身份证、手机号等需要唯一。
  • 检查约束(CHECK)可以定义更多的业务规则。例如,性别的取值只能为“男”或“女”,用户名必须大写等;
  • 默认值(DEFAULT)用于为字段提供默认的数据。例如,玩家注册时的级别默认为一级。

域完整性,是指给定列的取值范围(即输入的有效性),比如性别取值应为男或女。

强制域有效性的方法有:

  • 限制数据类型(通过数据类型): 建表时指定字段的数据类型,自然而然就限制了字段的取值范围。
  • 断言(通过 CHECK 约束和规则):check 约束,指定某个字段的取值范围。例如用户名必须大写、余额不能小于零等。
  • 默认值。
CREATE TABLE student (
        stuno CHAR(11) PRIMARY KEY ,
        stuname VARCHAR(20) ,
        stuage SMALLINT,
        stusex CHAR(1) CHECK (stusex in ('F','M'))  /* check约束,指定范围 */
        schno CHAR(3)  REFERENCES school(schno) ON CASCADE DELETE

)



-- 存储了正常价及折扣价,您想确保折扣价低于正常价
CREATE TABLE products (
    no INTEGER,
    name TEXT,
    price NUMERIC CHECK (price > 0),
    discounted_price NUMERIC CHECK (discounted_price > 0),
    CHECK (price > discounted_price)
);



-- 非空约束
CREATE TABLE t_nn(
  id INT NOT NULL,
  c1 VARCHAR(10)
);

-- Oracle、MySQL
ALTER TABLE t_nn MODIFY c1 VARCHAR(10) NOT NULL;
-- SQL Server
ALTER TABLE t_nn ALTER COLUMN c1 VARCHAR(10) NOT NULL;
-- PostgreSQL
ALTER TABLE t_nn ALTER COLUMN c1 SET NOT NULL;

-- SQLite 不支持修改字段的约束

-- 其中,id 在创建表时指定了非空约束;c1 字段通过 ALTER TABLE 语句增加了非空约束,注意不同数据库的语法实现。

现在互联网业务系统中,数据库往往是瓶颈,字段限制,往往可以在前端校验完成,外键约束,实体完整性等,可以在后端完成,不一定要完全依赖数据库的各种约束。这样可以有效减少数据库计算判断的压力。

实体完整性

实体(Entity)是一个数据对象,是指客观存在并可以相互区分的事物,如一个教师、一个学生或一个雇员等。一个实体在数据库中表现为表中的一条记录。通常情况下,它必须遵守实体完整性规则。

实体完整性,是指使用**主键**来唯一地标识一个实体。

在关系数据库中,一条记录代表一个实体。而实体是可以相互区分、识别的,也即它们应具有某种唯一性标识(该标识不能取相同的值,也不能为空)。

实体完整性强制表的标识符列或主键的完整性(通过索引、UNIQUE 约束、PRIMARY KEY 约束或 IDENTITY 属性)。

  • 主键约束(Primary Key),用于唯一标识表中的每一行数据。例如学生信息表中,学号通常作为主键。主键字段不能为空并且唯一,每个表可以有且只能有一个主键。

实体完整性检查和违约处理 使用 PRIMARY KEY 短语定义关系的主码后。每当用户插入或更新记录时,DBMS 都会自动进行实体完整性检查:

  • 检查主码是否唯一,若不唯一便拒绝插入或修改。
  • 检查主码的各个属性是否为空,若存在为空便拒绝插入或修改。

引用完整性

引用完整性/参照完整性,就是定义外键与主键之间的引用规则。

参照的完整性要求**关系中不允许引用不存在的实体**。也称引用完整性,参照完整性描述了实体间的联系。参照完整性一般是指多个实体表之间的引用关系。

参照完整性规则其实就描述了外键的定义。这个规则实际上是说:如何 B 参照 A,那么 A 必须存在。

  • 外键约束(Foreign Key),用于建立两个表之间的参照完整性。例如学生属于班级,学生信息表中的班级字段是一个外键,引用了班级表的主键。对于外键引用,被引用的数据必须存在;学生不可能属于一个不存在的班级。
-- 部门表
CREATE TABLE dept(
     department_id    INTEGER NOT NULL PRIMARY KEY,
     department_name  CHARACTER VARYING(30) NOT NULL
    ) ;
-- 员工表
CREATE TABLE emp(
     employee_id    INTEGER NOT NULL PRIMARY KEY,
     first_name     CHARACTER VARYING(20),
     last_name      CHARACTER VARYING(25) NOT NULL,
     salary         NUMERIC(8,2),
     manager_id     INTEGER,
     department_id  INTEGER,
     CONSTRAINT     fk_emp_dept  FOREIGN KEY (department_id) REFERENCES dept(department_id)
    ) ;

-- 外键约束中被引用的表称为父表(dept),外键所在的表称为子表(emp)。

-- 外键约束 fk_emp_manager 引用了 emp 表自身,用于维护员工和经理之间的联系。如果 emp 中已经存在数据,必须满足该外键约束的条件,否则无法添加该约束。
ALTER TABLE emp ADD CONSTRAINT fk_emp_manager   FOREIGN KEY (manager_id)  REFERENCES emp(employee_id)
    ;



CONSTRAINT constraint_name
FOREIGN KEY (column_name)
REFERENCES parent_name(column_name)
ON DELETE [NO ACTION|RESTRICT|CASCADE|SET NULL|SET DEFAULT]
ON UPDATE [NO ACTION|RESTRICT|CASCADE|SET NULL|SET DEFAULT];

-- NO ACTION 表示如果父表上的 DELETE 或者 UPDATE 操作违反外键约束,返回错误;在事务提交(COMMIT)时检查。
-- ESTRICT 表示如果父表上的 DELETE 或者 UPDATE 操作违反外键约束,返回错误;在语句执行时立即检查。
-- CASCADE 表示如果父表上执行 DELETE 或者 UPDATE 操作,级联删除或者更新子表上的记录。
-- SET NULL 如果父表上执行 DELETE 或者 UPDATE 操作,将子表中的外键字段设置为 NULL。
-- SET DEFAULT 如果父表上执行 DELETE 或者 UPDATE 操作,将子表中的外键字段设置为默认值。


-- Oracle 不支持任何外键的级联更新操作;MySQL 中的 NO ACTION 和 RESTRICT 效果相同,都是在语句执行时立即检查。

断言

在 SQL 中可以使用数据定义语言中的CREATE ASSERTION语句,通过声明性断言(declarative assertions)来制定更具一般性的约束。

在断言创立后,任何对断言中所涉及的关系的操作都会触发 DBMS 对断言的检查,任何使断言不为真值的操作都会被拒绝执行。

CREATE ASSERTION <断言名> <CHECK子句>

-- 示例,限制一门课最多60个学生选修
CREATE ASSERTION ASSE_SC_CNUM2
    CHECK (60 >= ALL(SELECT COUNT(*) FROM SC GROUP BY cno, term))

在日常业务开发中,会经常遇到需要保证唯一性的数据业务,如用户注册业务。

一般注册业务中允许用户以手机号或 email 注册账号,且需要保证唯一,不允许重复注册。当用户输入手机号或 email 登录时,程序会判定输入信息的存在与否性,存在则走登录,不存在则走注册。而保证唯一性就不仅仅需要在程序端做判断,还需要 MySQL 的唯一索引去做最后一道防线。

约束是业务检查,比方说非空约束,check 约束,那是检查字段是否非空,是否符合 check。

索引是一种数据结构, rowid 与字段值的键值对. 两个东西的效果是一样。

在建立唯一约束时,会自动创建一个唯一索引。并且,失效该约束时,索引自动删除。

而创建唯一索引则不会自动创建唯一约束。因此在建表时,如果要创建唯一索引,最好先建唯一索引再创建唯一约束。这样的话,在进行大批量数据插入时,可以先失效约束,插入完成后再进行索引重建。

约束主要是为了保证数据的完整性,索引主要是为了辅助查询。

唯一索引用于限制索引字段值的唯一性,或者是多个字段组合值的唯一性。CREATE UNIQUE INDEX 创建唯一索引。

唯一约束可以是单个字段,也可以是多个字段的组合,设置唯一约束后,INSERT 或 UPDATE 时如果表中唯一键字段中已存在该数据,则拒绝该行数据的 INSERT 或 UPDATE。但是数据库中 NULL 并不等于 NULL,所以唯一键中如果没有 NOT NULL 约束,则可以在唯一键中 INSERT 或 UPDATE 任意多个 NULL。

在 MySQL 中,唯一索引树是一个非聚簇索引,每次插入数据时,都会在唯一索引树上进行遍历查找该插入值是否唯一,这也就是为什么会影响 insert 的速度,因为多一步遍历判断唯一性。

如果你想向表中增加唯一约束,必须要考虑表中已存在的数据可能存在重复数据。重复的数据有两种理解方式:

  • 方式一:严格意义上的唯一,NULL 不等于 NULL,即(1,NULL)和(1,NULL)不是重复数据。

  • 方式二:非严格意义上的唯一,NULL 等于 NULL,即(1,NULL)和(1,NULL)是重复数据。

所以向表中增加唯一约束必须要删除这些重复数据,或者将重复数据删除到唯一。

  • 情况一:删除严格意义上的重复

  • 第一步:清空测试表,写入一些测试数据。

码是数据系统中的最基本的概念。所谓码就是能唯一标识实体的属性,它是整个实体集的性质,而不是单个实体的性质。

一个元组的属性值必须是能够唯一区分元组的,换句话说,一个关系中没有两个元组在所有属性上的取值都相同。即表中没有完全重复的行。

  • 超码:一个或多个属性的集合,这些属性可以使我们在一个关系中唯一标识一个元组,这个属性集合被称为超码(super key),如果K是超码,那么K的任意超集也是超码。

  • 候选码:存在这样得一些超码,即他们得任意真子集都不能成为超码。这样的最小超码称为候选码(candidate key)

  • 主码:主码(primary key,中文又称主键)是被数据库设计者被选中的,主要用来在一个关系中区分不同元组的候选码。

  • 外码:外码(foregin key,中文又称外键),假设在某个表R1中存在一个属性,这个属性是另外一个表R2的主键。则这个属性在R1上被称为参照R2的外码(外键)。 外码用于表示两个或多个实体间的关联关系。外码实际上是关系中的一个或多个属性,这些属性引用其他关系的主码或(候选码)

函数依赖

设一个关系为 R(U),X 和 Y 为属性集 U 上的子集,若对于 X 上的每个值都有 Y 上的一个唯一值与之对应,则称 X 和 Y 具有函数依赖关系,并称 X 函数决定 Y,或称 Y 函数依赖于 X,记作 X→Y,称 X 为决定因素。

 数据依赖是一个关系内部属性与属性之间的一种约束关系。这种约束关系是通过属性间值的相等与否体现出来的数据间的相关联系。

 函数依赖指的是在数据库中,各种不同属性(或者属性集合)关系间的一种"约束",也可以看作属性之间的一种映射,也是一种"依赖性",存在"当且仅当"的前提。

 若在一张表中,在属性(或属性组)X的值确定的情况下,必定能确定属性Y的值,那么就可以说Y函数依赖于X,写作 X → Y。

 也就是说,在数据表中,不存在任意两条记录,它们在X属性(或属性组)上的值相同,而在Y属性上的值不同。这也就是“函数依赖”名字的由来。
部分函数依赖

设一个关系为 R(U),X 和 Y 为属性集 U 上的子集,若对于 X 上的每个值都有 Y 上的一个唯一值与之对应,则称 X 和 Y 具有函数依赖关系,并称 X 函数决定 Y,或称 Y 函数依赖于 X,记作 X→Y,称 X 为决定因素。

传递函数依赖

设一个关系为 R(U),X 和 Y 为属性集 U 上的子集,若对于 X 上的每个值都有 Y 上的一个唯一值与之对应,则称 X 和 Y 具有函数依赖关系,并称 X 函数决定 Y,或称 Y 函数依赖于 X,记作 X→Y,称 X 为决定因素。

第一范式

第一范式最好理解,如果一个关系中的所有属性的域都是原子的,那么这个关系属于第一范式。(属性不可拆分)。

 在当前的任何关系数据库管理系统RDBMS中,设计出不符合第一范式的数据库都是不可能的。因为这些DBMS不允许你把数据库表的一列再分成二列或多列。


```SQL
(学号,姓名,系名,系主任,课程名,分数)
 主码(学号,课程名)

-- 对于(学号,课名) → 姓名,有 学号 → 姓名,存在非主属性 姓名 对码(学号,课名)的部分函数依赖。
-- 对于(学号,课名) → 系名,有 学号 → 系名,存在非主属性 系名 对码(学号,课名)的部分函数依赖。
-- 对于(学号,课名) → 系主任,有 学号 → 系主任,存在非主属性 系主任 对码(学号,课名)的部分函数依赖。
```
第二范式

非主属性(除主码以外的所有属性)

消除了非主属性对于主码的部分函数依赖。判断的依据实际上就是看数据表中是否存在非主属性对于主码的部分函数依赖。

```SQL
选课(学号,课名,分数) 主码(学号,课名)
学生(学号,姓名,系名,系主任) 主码(学号)

-- 主码为一个属性时,不存在非主属性对主码的部分函数依赖。
-- 但是存在传递函数依赖。
```

(学号,学生姓名、年龄、性别、课程、课程学分、系别、学科成绩,系办地址、系办电话 )
第三范式

ER 实体联系图

实体联系图(E-R diagram)ERD 可以用图形化表示数据库的全局逻辑结构。

形式化关系查询

关系代数是一种过程化查询语言。它包含一个运算的集合。这些运算以一个或多个关系做为输入,产生一个新关系做为结果。

关系代数本质上是 SQL 在数学上的表示,它和 SQL 的表达能力是完全等价的。因此可以说只有学懂了关系代数,才能在数学层面理解 SQL、才有可能写出符合逻辑的 SQL 语句。

关系演算则和关系代数相反,它更像是一整“块”数据在各种条件的约束下,像洋葱一样越变越“小”的过程,因此学懂了关系演算,有助于理解 SQL 是如何被执行的。

关系数据语言可以分为三类: 关系代数、关系演算和介于关系代数与关系演算之间的语言 SQL。关系代数的运算对象是关系,运算结果也是关系。

关系代数用到的运算符包括四类:集合运算符、专门的关系运算符、算术比较运算符和逻辑运算符。其中比较运算符和逻辑运算符是用来辅助运算的专门关系运算符

基本运算:

  • 选择运算:对应 SELECT 条件查询,选择运算选出来给定谓词的元组。它对应于 SQL 中的 WHERE 子句。按照指定的条件筛选出满足条件的元组(行)。

  • 符号:σ

  • 表达式:σ_F​(R)
  • 逻辑:从关系 R 中选择使得逻辑表达式 F 为真的所有元组 t。

  • 选择运算是从关系的水平方向进行的,即它过滤掉不满足条件的行。

  • 选择运算的结果仍然是一个关系,这意味着结果可以作为另一个运算的输入。
  • 选择运算不会改变元组的属性,它只是简单地包含或排除整个元组。

  • 投影运算:对应于 SQL 中的 SELECT 语句(特定列的选择)。投影用于提取关系中的特定列,并创建一个新的关系,该关系仅包含指定的列。

  • 集合并运算: 对应 UNION 查询, - 符号:∪ - 表达式:σ_F​(R) - 逻辑:从关系 R 中选择使得逻辑表达式 F 为真的所有元组 t。

    R和S的并,R∪S,是在R或S或两者中的元素的集合

 一个元素在并集中只出现一次  R 和 S 必须同类型(属性集相同、次序相同,但属性名 可以不同)

  • 集合差运算:
  • 笛卡尔积运算:

\Sigma

数据库对象

数据库对象是数据库的组成部分,是指在数据库中可以通过 SQL 进行操作和使用的对象。

数据库中的基本对象是表(TABLE)。但是除了表以外,数据库中还有很多其他的对象,例如索引(INDEX)、视图(VIEW)、存储过程、触发器(Trigger)、游标、函数,等。

这些数据库对象对于提高数据查询的效率、提升数据的安全性与完整性以及实现数据操作代码的共享性和数据操作的灵活性等方面起到了良好的辅助作用。在这里将对数据库中的部分对象:索引、视图、存储过程、触发器进行详细介绍。

在关系数据库中,表也被叫做关系,它是一种行列形式的二维表。其实就是一系列二维数组的集合,用于存储各种信息。

视图

SQL 允许通过查询来定义“虚关系”。它在概念上包含查询的结果, 视图是一种虚关系,它在概念上包含查询的结果,类似一种子查询的中间表或者某个表的子表,它在需要的时候才会被计算。虚关系并不预先计算并存储,而是请求虚关系的时候去执行查询计算出来。

数据库中仅存放了视图的定义,不存放视图对应的数据。也就是说视图并不存放数据。通过视图看到的数据实际上是执行视图定义时的数据查询命令而查询出的存放在基本表中的数据。

同一张基本表,根据不同用户的需求,可以创建不同的视图。

视图的主要功能:

(1)视图能够简化用户的操作。对于基本表中的一些需要频繁操作的数据,通过定义视图,可以实现简单地按视图名就可以对其进行访问,而不需要再写众多查询命令。

(2)视图能够使用户从多种角度看待同一数据。因为不同用户使用同一数据库时,所定义的视图不同,看到的数据也不同。

(3)视图能够实现数据的逻辑独立性。数据的逻辑独立性是指当数据库的逻辑结构发生改变时(比如给基本表添加了新的字段),用户和应用程序可以不受影响地继续使用数据。

(4)视图能够对机密数据提供安全保护。在用户查询数据库的数据时,只提供必要的数据视图,防止未经许可的用户访问敏感数据。

-- 视图的定义,其中 query expression 是任意合法的查询表达式
create view view_name(column1,column2 ...) as <query expression>


-- 列出Physics系在2009年秋季学期开设的所有课程段
create view physics_fall_2009 as
    select course, course_id, sec_id, building, room_number
        from course, section
    where course.course_id = section.course_id
        and course.dept_name = 'Physics'
        and course.semester = 'Fall'
        and course.year = '2009'



-- 物化视图(Materialization VIEW)
-- 由于视图中的数据是从表中查询出来的,所以一旦表中的数据发生改变,视图就会过期,所以视图中的数据也要发生改变。
-- 这样的视图称为物化视图(materialized view)

-- 物化视图维护(materialized view maintenance)
-- 保持视图物化一直在最新状态的过程称为物化视图维护,物化视图维护的策略大概有几种:
--  1. 每当数据变更时就进行视图物化
--  2. 周期性的进行视图物化
--  3. 当需要访问视图时才进行视图物化


-- 当定义视图的时候,一般来说数据库只会存储该视图定义的查询语句。与此相反,物化视图是一个其内容已计算并存储的视图。
-- 物化视图带来了冗余数据,因为其内容可以通过视图定义和表中的数据来查询得到。

-- MySQL的视图不是一种物化视图,它相当于一个虚拟表,它本身并不存储数据,它是作为一个select语句保存在数据字典中的。
-- 通过视图,可以展现基表的部分数据;视图数据来自定义视图的查询中使用的表,使用视图动态生成。
-- 这带来的问题是使用视图并不能将常用数据分离出来,优化查询速度。
-- 且操作视图的很多命令都与普通表一样,这会导致在业务代码中无法通过sql区分表和视图,使代码变得复杂。


-- 实现视图的方式有两种,分别为合并算法和临时表算法

-- 合并算法是指查询视图时将视图定义的sql合并到查询sql中,比如create view v1 as select * from user where sex=m;
-- 当我们要查询视图时,mysql会将select id,name from v1;合并成select id,name from user where sex=m……;

-- 临时表算法是先将视图查出来的数据保存到一个临时表中,查询的时候查这个临时表。不管是合并算法和临时表算法都会带来额外的开销。
-- 且如果使用临时表后会使mysql的优化变得很困难,比如索引。
-- 很多人本末倒置,复杂的SQL语句都是由于数据库表结构设计不合理造成的,不去反思为什么这么设计。