跳转至

5.3 数据表管理

基本管理

在上一节中数据库的管理推荐了“文件夹即数据库”的思路,类似的对于数据表也推荐“文件即数据表”的思路。但是这里仍然有一些细节值得考虑:

  1. 索引和数据文件应当放到在一起还是分作不同的文件?
  2. 数据表的 Schema 应该存储在数据文件中还是单独存储在元信息文件中?如果采用元信息文件,应该一张表一个文件还是所有表共用一个文件?

上述问题没有固定的答案,不同的选择会让你有着完全不同的数据组织方式。例如如果你将元信息单独放在元信息文件中,那么它们甚至不必基于页式文件系统与记录管理系统:这些文件通常较小,很可能写不满一页,完全可以每次读写时都全文件读写,将它们保存为自定义的二进制格式甚至是文本文件。

一种可能的采用较少文件的策略如下:

  • 对于一张表,所有信息存放在同一个文件中
  • 第一页存放元信息,包括表的 Schema、索引信息、表与各个索引数据页第一页的编号等
  • 创建表时新建该文件并完成第一页内容,删除表时删除该文件即可,删除索引时则需要修改元信息并将索引使用的页都标记为空页

一种可能得采用较多文件的策略如下:

  • 每一张表由一个数据文件、一个元信息文件、若干个索引文件组成
  • 数据文件与索引文件第一页便是数据页,表的 Schme、索引元信息等都存放在元信息文件中
  • 删除表时需要将上述文件都删除,删除索引是只需要修改元信息并删除索引文件

在确定了上述策略后,关于主外键等完整性约束的实现则比较自然,在增删这些约束时,在元信息中变动相应记录,如果涉及到索引的增删也一并完成;在对数据增删查改时也应该从数据表的元信息中获取这些约束,并根据约束的存在做相应检查。

进阶管理(选做)

选做部分中还涉及到增删改一列的 Schema 等操作,这属于更麻烦一些的操作,它或者会导致你在设计时的麻烦,或者会导致执行时的麻烦(低效)。注意为了避免引起不必要的误会,这部分文法我们从文法文件中移除了,你可以自行添加对应 Alter 文法,格式可以参考 MySQL文法,完成这部分选做内容需要包含以下支持:

  • 删除一列
  • 新增一列
  • 修改一列的列名、default、NULL 等元信息

在不考虑效率的情况下,这些实现可能相对容易,例如如果你采用了文件较多的数据表管理版本,那么在修改数据表 Schema 时,你可以将来的数据文件重命名,新建一个数据文件,将原来的数据依次插入到新文件中,完成后再删除旧文件。

但是这样的办法是很笨重的,很多时候我们都有更好的选择,例如对于删除一列的操作,我们可以在 Schema 中进行标记,知道每条记录中的某一部分是无效数据,而不直接操作数据文件;我们可以为所有记录都预留是否允许 NULL 的 flag,从而在修改 NULL / NOT NULL 时无需对记录进行操作;我们可以在最初创建表时就将记录设置为某些更大的长度以预留空间(采用类似于 std::vector 的思路在迫不得已时进行倍增扩容),从而在新增一列时我们只需要知道这些列都是 default 值,而不需要修改全部列内容。

注意,如果你真的采用了全部数据重新插入的办法,则这部分选做内容只能获取部分分数。

Authors: Congyuan Rao