JoeChenzzz

导航

mysql

1.事务

1)事务:事务是一个最小的不可再分的工作单元,是一组原子性的SQL操作,事务内的语句,要么全部执行成功,要么全部执行失败;通常一个事务对应一个完整的业务(例如银行账户转账业务,该业务就是一个最小的工作单元)

2)事务有四大特征(ACID):原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)

3)原子性:事务是一个最小的不可再分的工作单元,事务内的语句,要么全部执行成功,要么全部执行失败;事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样

  银行应用是解释事务的一个经典例子。假设一个银行的数据库有两张表:支票表和储蓄表。现在要从用户A的支票账户里转移200美元到她的储蓄账户,那么需要至少三个步骤:

  1. 检查支票账户的余额高于200美元
  2. 从支票账户余额中减去200美元
  3. 在储蓄账户余额中增加200美元

        以上三个步骤的操作必须打包在一个事务中,任何一个步骤失败,则必须回滚所有的步骤。

4)一致性:一致性是指数据总是处于一种有意义的一致状态;例如,账户A转了一笔钱到账户B,如果账户A的钱减少了,而账户B的钱没有增加,那么我们认为此时数据处于不一致的状态

5)隔离性:隔离性是指一个事务所做的修改在最终提交以前,对其他事务是不可见的;在前面的例子中,当还未执行完第三条语句时,此时有另外的一个账户汇总程序开始运行,则其看到支票帐户的余额并没有被减去200美元的

6)持久性:持久性是指一旦事务提交,则其所做的修改会永久保存到数据库

7)在事务处理的ACID属性中,一致性是最基本的属性,其它的三个属性都为了保证一致性而存在的

8)为了实现原子性和持久性,需要通过日志:将所有对数据的更新操作都写入日志,如果一个事务中的一部分操作已经成功,但以后的操作,由于断电/系统崩溃/其它的软硬件错误而无法继续,则通过回溯日志,将已经执行成功的操作撤销,从而达到“全部操作失败”的目的。最常见的场景是,数据库系统崩溃后重启,此时数据库处于不一致的状态,必须先执行一个crash recovery的过程:读取日志进行REDO(重演将所有已经执行成功但尚未写入到磁盘的事务,保证持久性),再对所有到崩溃时尚未成功提交的事务进行UNDO(撤销所有执行了一部分但尚未提交的事务,保证原子性)。crash recovery结束后,数据库可以继续被使用

9)在多个事务并行进行的情况下,即使保证了每一个事务的原子性,仍然可能导致数据不一致的结果。例如,事务1需要将100元转入帐号A:先读取帐号A的值,然后在这个值上加上100。但是,在这两个操作之间,另一个事务2修改了帐号A的值,为它增加了100元。那么最后的结果应该是A增加了200元。但事实上,事务1最终完成后,帐号A只增加了100元

10)为了保证并发情况下的一致性,引入了隔离性,怎样实现隔离性,原则上无非是两种类型的锁

  • 悲观锁:悲观锁对数据被外界修改持保守态度,所以对当前事务将所有操作的对象加锁,操作完成后释放给其它对象使用。为了尽可能提高性能,发明了各种粒度(数据库级/表级/行级……)/各种性质(共享锁/排他锁/共享意向锁/排他意向锁/共享排他意向锁……)的锁。为了解决死锁问题,又发明了两阶段锁协议/死锁检测等一系列的技术
  • 乐观锁:乐观锁假设认为数据一般情况下不会造成冲突,只在数据进行提交更新的时候,才会正式对数据冲突进行检测。造成的结果是不同的事务可以同时看到同一对象(一般是数据行)的不同历史版本。如果有两个事务同时修改了同一数据行,那么在较晚的事务提交时进行冲突检测,如果发现冲突了,则返回用户错误的信息,让用户决定如何去做。乐观锁的实现方式有两种:
  1. 通过日志UNDO的方式来获取数据行的历史版本
  2. 简单地在内存中保存同一数据行的多个历史版本,通过时间戳来区分

参考资料:

https://www.zhihu.com/question/30272728

https://blog.csdn.net/xiaokang123456kao/article/details/75268240

2.隔离级别

查看mysql的默认隔离级别:mysql > select @@global.tx_isolation;

1)SQL标准中定义了四种隔离级别(级别递增):读未提交(Read Uncommitted)、读已提交(Read Committed)、可重复读(Repeatable Read)、可串行化(Serializable)

2)读未提交

  • 读未提交是指事务中的修改即使未提交,对其他事务也都是可见的(别人select可以显示你已经更改却还没有提交的数据的那种变化
  • 事务可以读取未提交的数据,这也称为脏读:事务A正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时事务B读到了这个发生变化但还未提交的数据,因为这个数据是还没有提交的数据,那么事务B读到是脏数据
  • 这个级别会导致很多问题,一般很少用
  • 读未提交下,任何操作都不会加锁

3)读已提交

  • 一个事务只能看见已提交事务所做的改变(别人select你已经更改但是还未提交的数据时,显示的是更改以前的状态,只有当你提交了,他再select才能看到变化)
  • 未提交读避免了脏读
  • 这个级别有时也叫不可重复读:事务A读取了一条数据,然后正在执行某种操作的时候,事务B将这条数据update提交了,然后事务A再次读取的时候,却发现这个数据改变了(和上一次读的时候不一样了)
  • 这个是大多数据库默认的隔离级别,但mysql不是
  • 读已提交下,数据读取时,对读取的数据不加锁,但是数据插入、修改和删除时,对这些数据加行锁,直到操作完成,解锁

4)可重复读

  • 可重复读保证一个事务里,多次读取(select)某一数据,都是一样的结果(别人select你已经更改了并提交了的数据,显示的还是他当初那个时刻获得的数据,反复select都不会变化,只有当他也提交了,出来再select才会发现变化)
  • 可重复读是mysql数据库的默认隔离级别
  • 可重复读避免了脏读,但无法解决幻读:事务A根据条件索引得到N条数据,然后事务B插入insert了M条符合事务A搜索条件的数据,导致事务A再次搜索发现有N+M条数据了,就像产生了幻觉一样,平白无故的多了几条数据,这就是幻行,形成幻读
  • 不可重复读是侧重于update/delete操作,而幻读侧重于insert操作
  • 在可重复读中,第一次读取到数据后,通过行锁将这些数据加锁,其它事务无法修改这些数据,就可以实现可重复读了,但这种方法却无法锁住insert的数据,所以会出现幻读
  • InnoDB存储引擎通过多版本并发控制MVCC(Multi - Version Concurrency Control,多版本并发控制)防止幻读,但是它防止的是快照读的幻读,还没防止当前读的幻读
  • 在InnoDB中,会在每行数据后添加两个额外的隐藏的值来实现MVCC,这两个值一个记录这行数据何时被创建,另外一个记录这行数据何时过期(或者被删除)。 在实际操作中,存储的并不是时间,而是事务的版本号,每开启一个新事务,事务的版本号就会递增
  • 通过MVCC机制,虽然让数据变得可重复读,但我们读到的数据可能是历史数据,是不及时的数据,不是数据库当前的数据,对于这种读取历史数据的方式,我们叫它快照读 (snapshot read),而读取数据库当前版本数据的方式,叫当前读 (current read)
  • 快照读:就是select

    select * from table ....;

  • 当前读:特殊的读操作,插入/更新/删除操作,属于当前读,处理的都是当前的数据,需要加锁

    select ……lock in share mode;

    select ……for update;

    insert;

    update ;

    delete;

  • 为了解决当前读中的幻读问题,MySQL事务使用了Next-Key锁,select不加lock in share mode之类的就使用mvcc,否则使用next-key
  • Next-Key锁是行锁和GAP锁(间隙锁)的合并,除了锁住数据行,还锁住了索引数据行之间的间隙,这样就无法在数据行之间insert新记录了,解决了当前读的幻读
  • mvcc的优势是不加锁,并发性高。缺点是不是实时数据;next-key的优势是获取实时数据,但是需要加锁

5)可串行化

  • 可串行化是指一个事务一个事务的执行,一个事务未执行完毕,另一个事务不能执行
  • 可串行化避免了脏读、幻读、不可重复读
  • 可串行化是最高的隔离级别,它会在读取的每一行数据上都加锁,可能会导致大量的超时和锁争用的问题,实际应用中很少采用

参考资料:

https://www.cnblogs.com/snsdzjlz320/p/5761387.html

https://www.cnblogs.com/Genesisx/p/9139175.html

3.事务的类型

  从事务理论的角度来看,可以把事务分为以下几种类型:扁平事务带有保存点的扁平事务链事务嵌套事务分布式事务

3.1扁平事务

1)扁平事务(Flat Transactions)是事务类型中最简单的一种,这可能是使用最频繁的事务,故所有数据库系统都实现了对扁平事务的支持

2)在扁平事务中,所有操作都处于同一层次,其由BEGIN WORK开始,由COMMIT WORKROLLBACK WORK结束,其间的操作要么都执行,要么都回滚,因此扁平事务是应用程序称为原子操作的的基本组成模块

3.2带有保存点的扁平事务

1)扁平事务的主要缺点是不能提交或者回滚事务的某一部分,例如用户在旅行网站上进行自己的旅行度假计划,用户设想从杭州到意大利的佛罗伦萨,这两个城市没有直达的班机,需要用户预订并转呈航班,需要或者搭火车等待。用户预订旅行度假的事务为

BEGIN WORK:

  • S1:预订杭州到上海的高铁
  • S2:上海浦东国际机场坐飞机,预订到米兰的航班
  • S3:在米兰转火车前往佛罗伦萨,预订去佛罗伦萨的火车

但是当用户执行到S3时,发现由于飞机到达米兰的时间太晚,已经没有当天的火车,这时用户希望在米兰当地住一晚,第二天出发去佛罗伦萨。这时如果事务为扁平事务,需要回滚之前S1 S2 S3的三个操作,这个代价明显很大,因为当再次进行该事务是,S1 S2的执行计划是不变的,也就是说,如果支持有计划的回滚操作,那么不需要终止整个事务,因此就出现了带有保存点的扁平事务(Flat Transactions with Savepoints)

2)带有保存点的扁平事务允许在事务执行过程中回滚同一事务中较早的一个状态,以避免在执行过程中出现某种错误并不会导致所有的操作都无效,放弃整个事务不合乎要求,开销太大

3)带有保存点的扁平事务可以设置一些保存点来记住事务当前的状态,以便当之后发生错误时,事务能回到保存点当时的状态

4)保存点用SAVE WORK函数来建立,当出现问题时,保存点能用作内部的重启动点,根据应用逻辑决定是回到最近一个保存点还是其他更早的保存点

5)对于扁平的事务来说,其实隐式地设置了一个保存点,那就是事务的最开始时,不过它也只有这一个保存点

3.3链事务

1)带有保存点的扁平事务存在的问题:当带有保存点的扁平事务的保存点是易失的(系统崩溃时),这意味着当进行恢复时,事务需要从最开始处重新执行,而不能从最近的一个保存点继续执行

2)链事务(Chained Transactions)可视为保存点模式的一种变种,它是指一个事务由多个子事务链式组成,前一个子事务的提交操作和下一个子事务的开始操作合并成一个原子操作,这意味着下一个事务将看到上一个事务的结果,就好像在一个事务中进行的一样,这样,在提交子事务时就可以释放不需要的数据对象,而不必等到整个事务完成后才释放

3)链事务与带保存节点的扁平事务不同的是:链事务中的回滚仅限于当前子事务,相当于只能恢复到最近的一个保存节点,而带保存节点的扁平事务能回滚到任意正确的保存点,这样的话,当发生系统崩溃时,链事务不需要回到链表的开头,只需要回到之前已提交的最后一个子事务的下一个子事务

3.4嵌套事务

1)嵌套事务(Nested Transaction)是一个层次结构框架,由一个顶层事务控制着各个层次的事务,顶层事务之下的称为子事务,其控制着每一个局部的操作,子事务本身也可以是嵌套事务

2)Moss的理论下,嵌套事务有以下性质:

  • 嵌套事务是由若干事务组成的一棵树,子树既可以是嵌套事务也可以是扁平事务
  • 处在叶节点的事务是扁平事务,但是每个事务从根到叶节点的距离可以说是不同的
  • 位于根节点的事务称为顶层事务,其他称为子事务。事务的前驱称(predecessor)为父事务(parent),事务的下一层称为儿子事务(child)
  • 子事务既可以提交也可以回滚。但是它的提交操作并不马上生效。除非其父事务已经提交。因此可以推论出,任何子事务都在顶层事务提交后才真正的提交
  • 树中的任意事务回滚会引起它的所有子事务一同回滚,所以子事务仅保留ACI特性而不具有D特性

3)在Moss的理论中,实际的工作是交由叶子节点完成,即只有叶子节点的事务才能才能访问数据库、发送信息、获取其他类型的资源。而高层的事务仅负责逻辑控制。决定合适调用相关的子事务

3.5分布式事务

1)分布式事务通常是一个分布式环境下运行的扁平事务,因此需要根据数据所在位置访问网络中的不同节点

2)假如一个用户在ATM机上进行跨行转账,例如持卡人从招商银行存储卡转账10000元到工商银行的存储卡。这种情况下,可以将ATM机视为节点A,招商银行的后台数据库视为节点B,工商银行的后台数据库视为C,这个转账的操作可分解为以下的步骤:

  • 节点A发出转账命令
  • 节点B执行存储卡中的余额减去10000
  • 节点C执行存储卡终端的余额增加10000
  • 节点A通知用户操作完成或失败

  这里需要使用到分布式事务,因为节点A不能通过一台数据库就完成任务,其需要访问网络中两个节点的数据库,而在每个节点的数据库执行的实务操作有都是扁平的,对于分布式事务,其同样需要满足ACID特性,要么都发生,要么都失败。对于上述例子,如果第2、3步中任何一个操作失败,都会导致整个分布式事务回滚

参考资料

https://www.cnblogs.com/olinux/p/5181550.html

4.数据库和数据库实例

1)数据库是文件的集合,是依照某种数据模型组织起来存放于耳机存储器中的数据集合

2)数据实例是程序,是位于用户和操作系统之间的一层管理软件,用户对数据的任何操作都是在数据库实例下进行的

5.关系型数据库和非关系型数据库

6.关系型数据库的主键和外键

1)关系型数据库中的表中可以有若干个字段,若某个字段组(注意是字段组,当然组里也可能只有一个字段)能唯一标识一条记录,则该字段就可以成为一个主键

2)成绩表中的学号不是成绩表的主键,但它和学生表中的学号相对应,并且学生表中的学号是学生表的主键,则称成绩表中的学号是学生表的外键

3)非关系型数据库没有主键和外键

4)主键不允许重复,不允许为空

5)只有主键才能设置自动增长,自动增长一定是主键,主键不一定需要自动增长

6)一张表的外键是另一表的主键,,外键允许重复的,允许为空

7)主键用于唯一标识一条记录,外键用于与另一张表的关联

7.数据库的索引

1)索引是存储引擎用于快速找到记录的一种数据结构

2)索引的查看

show index from 表名

7.1底层实现

1)B+树索引

2)哈希索引

  • 哈希索引的过程:简单地说,哈希索引就是采用一定的哈希算法,把键值换算成哈希值,检索时不需要类似B+树那样从根节点到叶子节点逐级查找,只需一次哈希算法即可立刻定位到相应的位置,速度非常快

  • mysql中,只有Memory引擎支持哈希索引(当然它也支持B+树索引)

3)B+树索引和哈希索引的选择:

  • 如果是等值查询,那么哈希索引明显有绝对优势,因为只需要经过一次算法即可找到相应的键值;当然了,这个前提是,键值都是唯一的。如果键值不是唯一的,就需要先找到该键所在位置,然后再根据链表往后扫描,直到找到相应的数据;

  • 从示意图中也能看到,如果是范围查询检索,这时候哈希索引就毫无用武之地了,因为原先是有序的键值,经过哈希算法后,有可能变成不连续的了,就没办法再利用索引完成范围查询检索;

  • 同理,哈希索引也没办法利用索引完成排序,以及like ‘xxx%’ 这样的部分模糊查询(这种部分模糊查询,其实本质上也是范围查询);

  • 哈希索引也不支持多列联合索引的最左匹配规则

  • B+树索引的关键字检索效率比较平均,不像B树那样波动幅度大,在有大量重复键值情况下,哈希索引的效率也是极低的,因为存在所谓的哈希碰撞问题

7.2索引的分类

  mysql提供多种索引类型供选择:普通索引唯一性索引主键索引全文索引

1)普通索引:普通索引的唯一任务是加快对数据的访问速度,因此,应该只为那些最经常出现在查询条件(WHERE column=)或者排序条件(ORDERBY column)中的数据列创建索引

2)唯一性索引:如果确定某个字段只包含彼此各不相同的值,在为这个数据列创建索引的时候,就应该用关键字UNIQUE把它定义为一个唯一性索引,mysql会在有新纪录插入数据表时,自动检查新纪录的这个字段的值是否已经在某个记录的这个字段里出现过了。如果是,mysql将拒绝插入那条新纪录。也就是说,唯一性索引可以保证数据记录的唯一性

3)主键索引:必须为主键字段创建一个索引,这个mysql索引就是所谓的“主索引”。主索引唯一性索引唯一区别是:前者在定义时使用的关键字是PRIMARY而不是UNIQUE

4)全文索引:全文检索是将存储于数据库中的整本书中的任意内容找出来的技术,是利用查询关键字和查询列内容之间的相关度进行检索

参考资料:

https://www.cnblogs.com/heiming/p/5865101.html

8.联结

1)内部联结:内部联结又称为等值联结,将两个表存在联结关系的字段符合联结关系的那些记录形成记录集的联结,用INNER JOIN 指定表之间的关系,用ON子句指定联结条件

 这个其实和语句SELECT WHERE一样:

2)自联结:自己和自己的联结

3)自然联结:自然联结其实和内部联结一样,只是去除重复的列,在以前的版本有natural join 来实现自然联结,而在最新版本的MySQL中,不再支持natural join,mysql把这项工作交给了用户

4)外部联结:外部联结可以显示在相关表中没有关联的行

如果我们使用内部联结,就不会出现"Fox"这个人的信息:

但是想要显示所有人的信息,尽管“Fox”没有addr,这时候可以使用外部联结

外部联结分为LEFT OUTER JOIN (左外部联结)和RIGHT OUTER JOIN(右外部联结),这里使用LEFT OUTER JOIN:

上边的语句使用了LEFT OUTER JOIN。它以左边的表(也就是Name)作为基准,会选择Name中的所有行,如果该行在Address表中不存在相应的记录,就会以null值(必须要支持null值)显示

同理,RIGHT OUTER JOIN 是以右边的表作为基准

参考资料:

https://blog.csdn.net/pinkfriday/article/details/79038381

9.mysql数据库的体系结构

10.InnoDB存储引擎和MyISAM存储引擎的特点与区别

查看mysql提供引擎:      mysql> show engines;
查看mysql当前默认引擎:   mysql> show variables like '%storage_engine%';
查看某个表用了什么引擎:   mysql> show create table 表名\G;  \\ \G的作用是将查到的结构旋转90度变成纵向

1)Innodb引擎特点:

  • InnoDB引擎是新版本mysql的默认引擎
  • InnoDB支持事务的ACID四大特性
  • InnoDB支持行级锁(行级锁:可以对指定的行进行锁定,其他进程还是可以对表中的其他行进行操作)
  • InnoDB支持外键
  • InnoDB把数据和索引存放在表空间里面,跨平台可以直接拷贝使用
  • InnoDB没有保存表的总行数,如果使用select count(*) from table;就会遍历整个表,消耗相当大,但是在加了wehre条件后,myisam和innodb处理的方式都一样。

2)MyIASM引擎特点:

  • MyIASM不支持事务
  • MyIASM只支持表级锁(表级锁:直接锁定整张表,在锁定期间,其他进程无法对该表进行写操作)
  • MyISAM下的表被存放在三个文件:.frm 用于存储表的定义,.MYD用于存放数据,.MYI用于存放表索引,跨平台很难直接拷贝使用
  • MyISAM保存有表的总行数,如果select count(*) from table;会直接取出出该值

3)InnoDB引擎和MyIASM引擎的选择:

  • 根据需求选择:外键,事务支持,查询模式,数据大小、全文索引
  • 大尺寸的数据选择InnoDB引擎,因为它支持事务处理和故障恢复,数据库的大小决定了故障恢复的时间长短,InnoDB可以利用事务日志进行数据恢复,这会比较快,而MyISAM则很慢
  • 大批的insert语句在MyISAM下会快一些,但是update语句在InnoDB下则会更快一些,尤其是在并发量大的时候
  • 如果需要全文索引,那么通常来说MyISAM是好的选择,因为这是系统内建,而InnoDB可能要使用插件

11.关系型数据库的范式

1)范式是指数据表的表结构设计标准的级别

2)目前关系数据库有六种范式,满足最低要求的范式是第一范式(1NF),在1NF的基础上进一步满足更多要求的称为第二范式(2NF),其余范式以次类推。一般说来,数据库只需满足第三范式(3NF)就行了

3)第一范式(1NF):第一范式要求数据表中的每一列(每个字段)必须是不可拆分的最小单元,也就是确保每一列的原子性,第一范式是所有关系型数据库最基本的要求

4)超码/超键:能唯一标识记录的属性或属性组

5)候选码/候选键:最小超码,不含多余属性

6)主码/主键:从候选码中挑一个出来做老大,它叫主码,至于选哪个是无所谓的

7)主属性:候选码中的任何一个属性

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

9)完全函数依赖:在一张表中,若 X → Y,且对于 X 的任何一个真子集X '(假如属性组 X 包含超过一个属性),X ' → Y 不成立,那么我们称 Y 对于 X 完全函数依赖,记作:

10)部分函数依赖:在一张表中,若 X → Y,若存在一个X的真子集X '(假如属性组 X 包含超过一个属性),X ' → Y 成立,那么我们称 Y 对于 X 部分函数依赖,记作:

11)传递函数依赖:X,Y,Z是不同的属性,假如 Z 函数依赖于 Y,且 Y 函数依赖于 X ,那么我们就称 Z 对 X 传递函数依赖,记作

12)第二范式(2NF):满足1NF的前提下,2NF消除了非主属性对主码的部分函数依赖

  • 由表可知,主码为(学号课名),则主属性是学号、课名,非主属性是姓名系名系主任分数
  • 对于(学号,课名) → 姓名,有 学号 → 姓名,存在非主属性 姓名 对主码(学号,课名)的部分函数依赖,所以不满足2NF

  • 对于课程分数表,主码为(学号,课名),主属性是学号、课名,非主属性是分数,分数必须由学号+课名确定,所以不存在非主属性对主码的部分依赖,因此符合2NF
  • 对于学生表,主码是学号,主属性是学号,非主属性是姓名、系名、系主任,因为码只有一个属性,所以不可能存在非主属性对于主码的部分函数依赖,因此符合2NF

13) 第三范式(3NF):在满足2NF的前提下,3NF消除了非主属性对主码的传递函数依赖(着重于非主属性间的关系)

上面的学生表的主码是学号,主属性是学号,非主属性是姓名、系名、系主任,系主任依赖于系名,系名依赖于学号,所以存在非主属性系主任对于主码学号的传递,因此不符合3NF

再次分解得到,下面这三张表

14)要了解 BCNF 范式,那么先看这样一个问题:

  • 某公司有若干个仓库
  • 每个仓库只能有一名管理员,一名管理员只能在一个仓库中工作
  • 一个仓库中可以存放多种物品,一种物品也可以存放在不同的仓库中。每种物品在每个仓库中都有对应的数量

  • 候选码:(管理员,物品名),(仓库名,物品名),主属性:仓库名、管理员、物品名,非主属性:数量
  • 不存在非主属性对主码的部分函数依赖和传递函数依赖,故符合3NF

但该表还是会出现问题:

  • 插入异常:先新增加一个仓库,但尚未存放任何物品,是否可以为该仓库指派管理员?——不可以,因为物品名也是主属性,根据实体完整性的要求,主属性不能为空
  • 删除异常:某仓库被清空后,需要删除所有与这个仓库相关的物品存放记录,会带来什么问题?——仓库本身与管理员的信息也被随之删除了
  • 修改异常:如果某仓库更换了管理员,会带来什么问题?——这个仓库有几条物品存放记录,就要修改多少次管理员信息

造成此问题的原因:存在着主属性对于主码的部分函数依赖与传递函数依赖(在此例中就是存在主属性【仓库名】对于码【(管理员,物品名)】的部分函数依赖)

巴斯范式(BCNF):在满足3NF的前提下,BCNF消除了主属性对主码的部分函数依赖传递函数依赖(着重于主属性)

参考资料:

https://www.zhihu.com/question/24696366/answer/29189700

https://blog.csdn.net/pinkfriday/article/details/79038381

posted on 2019-03-15 11:55  JoeChenzzz  阅读(425)  评论(0)    收藏  举报