的列时,枚举和集合成员被指定为串。但是,这些成员在内部存放时作为数值,而且同样可作为数值来处理。这表示ENUM 和S E T类型比其他的串类型更为有效,因为通常可用数值运算而不是串运算来处理它们。而且这还表示ENUM 和SET 值可用在串或数值的环境中。 列定义中的ENUM 成员是从1开始顺序编号的。(0 被MySQL用作错误成员,如果以串的形式表示就是空串。)枚举值的数目决定了ENUM 列的存储大小。一个字节可表示256 个值,两个字节可表示65 536 个值。(可将其与一字节和两字节的整数类型T I N Y I N T、 UNSIGNED 和SMALLINT UNSIGNED 进行对比。)因此,枚举成员的最大数目为65 536(包括错误成员),并且存储大小依赖于成员数目是否多于256 个。在ENUM 定义中,可以最多指定65 535(而不是65 536)个成员,因为MySQL保留了一个错误成员,它是每个枚举的隐含成员。在将一个非法值赋给ENUM 列时,MySQL自动将其换成错误成员。下面有一个例子,可用mysql客户机程序测试一下。它给出枚举成员的数值顺序,而且还说明了NULL 值无顺序编号:
如果给SET 列赋予一个含有未作为集合成员列出的子串的值,那么这些子串被删除,并将包含其余子串的值赋予该列。在赋值给SET 列时,子串不需要按定义该列时的顺序给出。但是,在以后检索该值时,各成员将按定义时的顺序列出。假如用下面的定义定义一个S E T列来表示家具:
如果给这个列赋予“ c h a i r, couch, table”值,那么,“c o uc h”被放弃,因为它不是集合的成员。其次,以后检索这个值时,显示为“ table, chair”。之所以这样是因为MySQL针对所赋的值的每个子串决定各个二进制位并在存储值时将它们置为1。“c o uc h”不对应二进制位,则忽略。在检索时,MySQL按顺序扫描各二进制位,通过数值值构造出串值,它自动地将子串排成定义列时给出的顺序。这个举动还表示,如果在一个值中不止一次地指定某个成员,但在检索时它也只会出现一次。如果将“ lamp, lamp,lamp”赋予某个SET 列,检索时也只会得出“l a m p”。MySQL重新对SET 值中的成员进行排序这个事实表示,如果用一个串来搜索值,则必须以正确的顺序列出各成员。如果插入“ c h a i r, table”,然后搜索“c h a i r, table”,那么将找不到相应的记录;必须查找“ table, chair”才能找到。ENUM 和SET 列的排序和索引是根据列值的内部值(数值值)进行的。下面的例子可能会显示不正确,因为各个值并不是按字母顺序存储的:
NULL 值排在其他值前(如果是降序,将排在其他值之后)。如果有一个固定的值集,并且希望按特殊的次序进行排序,可利用ENUM 的排序顺序。在创建表时做一个ENUM 列,并在该列的定义中以所想要的次序给出各枚举值即可。如果希望ENUM 按正常的字典顺序排序,可使用C O N C AT( ) 和排序结果将列转换成一个非ENUM 串,如下所示:
4. 串列类型属性 可对CHAR 和VARCHAR 类型指定B I N A RY 属性使列值作为二进制串处理(即,在比较和排序操作区分大小写)。 可对任何串类型指定通用属性NULL 和NOT NULL。如果两者都不指定,缺省值为NULL。但是定义某个串列为NOT NULL 并不阻止其取空串。空值不同于遗漏的值,因此,不要错误地认为可以通过定义NOT NULL 来强制某个串列只包含非空的值。如果要求串值非 空,那么这是一个在应用程序中必须强制实施的约束条件。 还可以对除BLOB 和TEXT 类型外的所有串列类型用D E FA U LT 属性指定一个缺省值。如果不指定缺省值, MySQL会自动选择一个。对于可以包含NULL 的列,其缺省值为NULL。对于不能包含NULL 的列,除ENUM 列外都为空串,在ENUM 列中,缺省值为第一个枚举成员(对于SET 类型,在相应的列不能包含NULL 时其缺省值实际上是空集,不过这里空集等价于空串)。 2.2.4 日期和时间列类型 MySQL提供了几种时间值的列类型,它们分别是: DATE、DATE TIME、TIME、TIMES TAMP 和YEAR。表2-9 给出了MySQL为定义存储日期和时间值所提供的这些类型,并给出了每种类型的合法取值范围。YEAR 类型是在MySQL3.22版本中引入的。其他类型在所有MySQL版本中都可用。每种时间类型的存储需求见表2 - 10。每个日期和时间类型都有一个“零”值,在插入该类型的一个非法值时替换成此值,见表2 - 11。这个值也是定义为NOT NULL 的日期和时间列的缺省值。
TIMES TAMP 列的定义可包含对最大显示宽度M 的说明。表2 - 12给出了所允许的M 值的显示格式。如果TIMES TAMP 定义中省略了M 或者其值为0或大于14,则该列按TIMES TAMP(14) 处理。取值范围从1到13的M 奇数值作为下一个更大的偶数值处理。T I M E S TAMP 列的显示宽度与存储大小或存储在内部的值无关。TIMES TAMP 值总是以4 字节存放并按14 位精度进行计算,与显示宽度无关。为了明白这一点,按如下定义一个表,然后插入一些行,进行检索:
从表面上看,出现的行排序有误,第一列中的值全都相同,所以似乎排序是根据第二列中的值进行的。这个表面反常的结果是由于事实上, MySQL是根据插入T I M E S TAMP 列的全部14 位值进行排序的。MySQL没有可在记录建立时设置为当前日期和时间、并从此以后保持不变的列类型。如果要实现这一点,可用两种方法来完成: ■ 使用T I M E S TAMP 列。在最初建立一个记录时,设置该列为NULL,将其初始化为当前日期和时间:
无论以后何时更新此记录,都不能动该列:UPDATEtbl_name SET /* angthing BUT dt_col here */ WHERE ...如果想利用T I M E S TAMP 列既保存建立的时间值又保存最后修改的时间值,那么可用一个T I M E S TAMP 列来保存修改时间值,用另一个T I M E S TAMP 列保存建立时间值。要保证保存修改时间值的列为第一个T I M E S TA M P,从而在记录建立或更改时自动对其进行设置。使保存建立时间值的列为第二个T I M E S TA M P,并在建立新记录时将其初始化为NOW( )。这样第二个T I M E S TAMP 的值将反映记录建立时间,而且以后将不再更改。 3. YEAR 列类型 YEAR 是一个用来有效地表示年份值的1个字节的列类型。其取值范围为从1901到2 15 5。在想保存日期信息但又只需要日期的年份时可使用YEAR 类型,如出生年份、政府机关选举年份等等。在不需要完全的日期值时, YEAR 比其他日期类型在空间利用上更为有效。 YEAR 列的定义可包括显示宽度M 的说明,显示宽度应该为4 或2。如果YEAR 定义中省略了M,其缺省值为4。TINYINT 与YEAR 具有相同的存储大小(一个字节),但取值范围不同。要使用一个整数类型且覆盖与YEAR 相同的取值范围,可能需要SMALLINT 类型,此类型要占两倍的空间。在所要表示的年份取值范围与YEAR 类型的取值范围相同的情况下, YEAR 的空间利用率比SMALLINT 更为有效。YEAR 相对整数列的另一个优点是MySQL将会利用MySQL的年份推测规则把2 位值转换为4 位值。例如,97 与14 将转换为1997 和2 0 14。但要认识到,插入数值00 将得到0000 而不是2 0 0 0。如果希望零值转换为2 0 0 0,必须指定其为串“0 0”。
请注意,有前导零的值根据它们被指定为串或数有不同的解释。串“ 0 0 12 3 1”将视为一个六位数字的值并解释为DATE 的“2 0 0 0 - 12 - 3 1”和DATETIME 的“2000-12-3100:00:00”。而数0 0 12 3 1被认为12 3 1,这样的解释就有问题了。这种情形最好使用串值,或者如果要使用数值的话,应该用完全限定的值(即, DATE 用2 0 0 0 12 3 1,DATETIME 用2 0 0 0 12 3 10 0 0 0)。通常,在DATE、DATETIME 和T I M E S TAMP 类型之间可以自由地赋值,但是应该记住以下一些限制: ■ 如果将DATETIME 或T I M E S TAMP 值赋给DATE,则时间部分被删除。 ■ 如果将DATE 值赋给DATETIME 或T I M E S TA M P,结果值的时间部分被设置为零。 ■ 各种类型具有不同的取值范围。T I M E S TAMP 的取值范围更受限制( 1970 到2 0 3 7),因此,比方说,不能将1970 年以前的DATETIME 值赋给T I M E S TAMP 并得到合理的结果。也不能将2037 以后的值赋给TIMES TAMP。MySQL提供了许多处理日期和时间值的函数。要了解更详细的信息请参阅附录C。 6. 歧义年份值的理解 对于所有包括年份部分的日期和时间类型( DATE、DATE TIME、TIME STAMP、YEAR),MySQL将两位数字的年份转换为四位数字的年份。这个转换根据下列规则进行(在MySQL4.0 中,这些规则稍有改动,其中69 将转换为1969 而不是2069。这是根据X/Open UNIX 标准规定的规则作出的改动): ■ 00 到69 的年份值转换为2000 到2069。 ■ 70 到99 的年份值转换为1970 到1999。 通过将不同的两位数字值赋给一个YEAR 列然后进行检索,可很容易地看到这些规则的效果。下面是检索程序:
请注意,00 转换为0000 而不是2 0 0 0。这是因为0 是YEAR 类型的一个完全合法的值;如果插入一个数值,得到的就是这个结果。要得到2 0 0 0,应该插入串“ 0”或“0 0”。可通过C O N C AT( ) 插入YEAR 值来保证MySQL得到一个串而不是数。C O N C AT( ) 函数不管其参数是串或数值,都返回一个串结果。请记住,将两位数字的年份值转换为四位数字的年份值的规则只产生一种结果。在未给 定世纪的情况下,MySQL没有办法肯定两位数字的年份的含义。如果MySQL的转换规则不能得出您所希望的值,解决的方法很简单:即用四位数字输入年份值。MySQL有千年虫问题吗?MySQL自身是没有2000 年问题的,因为它在内部是按四位数年份存储日期值的,并且由用户负责提供恰当的日期值。两位数字年份解释的实际问题不是MySQL带来的,而是由于有的人想省事,输入歧义数据所引起的问题。如果您愿意冒险,可以继续这样做。在您冒险的时候,MySQL的猜测规则是可以使用的。但要意识到,很多时候您确实需要输入四位数字的年份。例如, p r e s i d e n t表列出了1700 年以来的美国总统,所以在此表中录入出生与死亡日期需要四位的年份值。这些列中的年份值跨了好几个世纪,因此,让MySQL从两位数字的年份去猜测是哪个世纪是不可能的。