分类: Mysql/postgreSQL
2008-09-05 20:58:14
在用基于日期信息的主-细目关系连接两个表时,决定是否需要时间值特别重要。假如您正在进行一项研究,包括一些对进入您的办公室的人进行测试的 题目。在一个标准的初步测试集之后,您可能会在同一天进行几个额外的测试,测试的选择视初步测试结果而定。您可能会利用一个主-细目关系来表示这些信息, 其中题目的标识信息和标准的初步测试存储在一个主记录中,而其他测试保存为辅助细目表的行。然后基于题目ID 与进行测试的日期将这两个表连接到一起。
在这种情况下必须回答的问题是,是否可以只用日期,或者是否需要既使用日期又使用时间。这个问题依赖于一个题目是否可以在同一天投入测试过程不止一次。如果是这样,那么应该记录时间(比方说,记录测试过程开始的时间),或者用DATETIME 列,或者分别用
DATE 和TIME 列(两者都必须填写)。如果一个题目一天测试了两次,没有时间值就不能将该题目的细目记录与适当的主记录进行关联。
我曾经听过有人声称“我不需要时间;我从不在同一天把一道题测试两次”。有时他们是对的,但是我也看到过这些人后来在录入同一天测试多次的题目的数据 后,反过来考虑怎样防止细目记录与错误的主记录相混。很抱歉,这时已经太迟了!有时可以在表中增加TIME 列来处理这个问题,不幸的是,除非有某些独立的数据源,如原书面记录,否则很难整理现有记录。此外,没办法消除细目记录的歧义,以便将它们关联到合适的主 记录上。即使有独立的信息源,这样做也是非常乱的,很可能使已经编写来利用表的应用程序出问题。最好是向表的拥有者说明问题并保证在创建他们的表之前进行 很好的描述。
有时具有一些不完整的数据,这会干扰列类型的选择。如果进行家谱研究,需要记录出生日期和死亡日期,有时会发现所能搜集到的数据中只是某人出生或死亡的年份,但没有确切的日期。如果使用DATE 列,除非有完整的日期值,否则不能输入日期。如果希望能够记
录所具有的任何信息,即使不完整也保存,那么可能必须保存独立的年、月、日字段。这样就可以输入所具有的日期成员并将没有的部分设为NULL。在 MySQL3.23 及以后的版本中,还允许DATE 的日为0 或者月和日部分为0。这样“模糊”的日期可用来表示不完整的日期值。
2.3.2 列值有特定的取值范围吗
如果已经决定从通用类别上选择一种列类型,那么考虑想要表示的值的取值范围会有助于将您的选择缩减到该类别中特定的类型上。假如希望存储整数值。这些整 数值的取值范围为0 到10 0 0,那么可以使用从SMALLINT 到BIGINT 的所有类型。如果这些整数值的取值
范围最多为 2 000 000,则不能使用S M A L L I N T,其选择范围从MEDIUMINT 到B I G I N T。需要从这个可能的选择范围中选取一种类型。当然,可以简单地为想要存储的值选择最大的类型(如上述例子中选择B I G I N T)。但是,一般应该为所要存储的值选择足以存储它的最小的类型。这样做,可以最小化表占用的存储量,得到最好的性能,因为通常较小列的处理比较大列的 快。如果不知道所要表示的值的取值范围,那么必须进行猜测或使用BIGINT 以应付最坏的情况。(请注意,如果进行猜测时使用了一个太小的类型,工作不会白做;以后可以利用ALTER TABLE 来将此列改为更大一些的类型。)
在第1章中,我们为学分保存方案创建了一个score 表,它有一个记录测验和测试学分的score 列。为了讨论简单起见,创建该表时使用了INT 类型,但现在可以看出,如果学分在0到100 的取值范围内,更好的选择应该是TINYINT UNSIGNED,因为所用的存储空间较小。数据的取值范围还影响列类型的属性。如果该数据从不为负,可使用UNSIGNED 属性;否则就不能用它。
选项。对于 用来表示某个固定值集合的串列,可以考虑使用ENUM 或SET 列类型。它们可能是很好的选项,因为它们在内部是用数来表示的。这两个类型上的运算是数值化的,因此,比其他的串类型效率更高。它们还比其他串类型紧凑、 节省空间。在描述必须处理的值的范围时,最好的术语是“总是”和“决不”(如“总是小于10 0 0”或“决不为负”),因为它们能更准确地约束列类型的选择。但在未确证之前,要慎用这两个术语。特别是与其他人谈他们的数据,而他们开始乱用这两个术语 时要注意。在有人说“总是”或“决不”时,一定要搞清他们说的确实是这个含义。有时人们说自己的数据总是有某种特定的性质,而其真正的含义是“几乎总是 ”。
例如,假如您为某些人设计一个表,而他们告诉您,“我们的测试学分总是0 到10 0”。根据这个描述,您选择了TINYINT 类型并使它为UNSIGNED 的,因为值总是非负的。然而,您发现编码录入数据库的人有时用- 1来表示“学生因病缺席”。呀,他们没告诉您这事。可能可以用NULL 来表示-1,但如果不能,必须记录- 1,这样就不能用UNSIGNED 列了(只好用ALTER TABLE 来补救!)。有时关于这些情形的讨论可通过提一些简单的问题来简化,如问:曾经有过例外吗?如果曾经有过例外情况,即使是只有一次,也必须考虑。您会发 现,和您讨论数据库设计的人总是认为,如果例外不经常发生,那么就没什么关系。然而在创建数据库时,就不能这样想了。需要提的问题并不是例外出现有多频 繁,而是有没有例外?如果有,必须考虑进去。
列类型的选择会在几个方面影响查询性能。如果记住下几节讨论的一般准则,将能够选出有助于MySQL有效处理表的列类型。
1. 数值与串的运算
数值运算一般比串运算更快。例如比较运算,可在单一运算中对数进行比较。而串运算涉及几个逐字节的比较,如果串更长的话,这种比较还要多。如果串列的值 数目有限,应该利用ENUM 或SET 类型来获得数值运算的优越性。这两种类型在内部是用数表示的,可更为有效地进行处理。例如替换串的表示。有时可用数来表示串值以改进其性能。例如,为了用 点分四位数(d o t t e d - q ua d)表示法来表示IP 号,如19 2 . 16 8 . 0 . 4,可以使用串。但是也可以通过用四字节的UNSIGNED 类型的每个字节存储四位数的每个部分,将IP 号转换为整数形式。这即可以节省空间又可加快查找速度。但另一方面,将IP 号表示为INT 值会使诸如查找某个子网的号码这样的模式匹配难于完成。因此,不能只考虑空间问题;必须根据利用这些值做什么来决定哪种表示更适合。
2. 更小的类型与更大的类型
更小的类型比更大的类型处理要快得多。首先,它们占用的空间较小,且涉及的磁盘活动开销也少。对于串,其处理时间与串长度直接相关。一般情况下,较小的 表处理更快,因为查询处理需要的磁盘I/O 少。对于定长类型的列,应该选择最小的类型,只要能存储所需范围的值即可。例如,如果MEDIUMINT 够用,就不要选择B I G I N T。如果只需要FLOAT精度,就不应该选择D O U B L E。对于可变长类型,也仍然能够节省空间。一个BLOB 类型的值用2 字节记录值的长度,而一个LONGBLOB 则用4 字节记录其值的长度。如果存储的值长度永远不会超过6 4 K B,使用BLOB 将使每个值节省2 字节(当然,对于TEXT 类型也可以做类似的考虑)。