使用枚举(ENUM)代替字符串
枚举列可以把一些不重复的字符串存储成一个预定义的集合。MySQL 在存储枚举时非常紧凑,会根据列表值的数量压缩到一个或者两个字节中。MySQL 在内部会将每个值在列表中的位置保存为整数,并且在表的.frm文件中保存 “数字-字符串” 映射关系的 “查找表”。
代码 3. 测试枚举的存储值
DROPTABLEIFEXISTSenum_test;CREATETABLEenum_test (e ENUM ('fish', 'apple', 'dog'));INSERTINTOenum_test (e)VALUES('fish'), ('dog'), ('apple');(1)SELECTe + 0FROMenum_test;SELECTeFROMenum_testORDERBYe;(2)SELECTeFROMenum_testORDERBYfield(e, 'apple', 'dog', 'fish');(3)
1三行数据实际存储为整数,而不是字符串。
2测试排序性
3根据定义的字符串排序
如果使用数字作为ENUM枚举常量,很容易导致混乱。尽量避免这么做。
DATETIME
保存大范围的值,从 1001 年到 9999 年,精度为秒。把日期和时间封装到格式为 YYYYMMDDHHMMSS 的整数中,与时区无关。使用 8 个字节的存储空间。
TIMESTAMP
保存从 1970 年 1 月 1 日午夜以来的秒数,和 UNIX 时间戳相同。TIMESTAMP只使用 4 个字节的存储空间,范围是从 1970 年到 2038 年。
TIMESTAMP显示的值也依赖于时区。MySQL 服务器、操作系统以及客户端连接都有时区设置。因此,存储值为 0 的TIMESTAMP在美国东部时区显示为 “1969-12-31 19:00:00”,与格林尼治时间差5个小时。
如果在多个时区存储或访问数据,TIMESTAMP和DATETIME的行为将会很不一样。前者提供的值与时区有关,后者则保留文本表示的日期和时间。
默认情况下,如果插入时没有指定第一个TIMESTAMP列的值,MySQL 则设置这个列的值为当前时间。
TIMESTAMP列默认为NOT NULL。
通常应该尽量使用TIMESTAMP,因为它比DATETIME空间效率更高。
可以使用BIGINT类型存储微秒级别的时间戳,或者使用DOUBLE存储秒之后的小数部分。
计数器表
可以利用CurrentHashMap分段锁的思想,将对同一个计算器的修改,打散到多个变量上,然后在求和。
DROPTABLEIFEXISTShit_counter;CREATETABLEhit_counter (
slot TINYINT UNSIGNEDNOTNULLPRIMARYKEY,
cnt INT UNSIGNEDNOTNULL
)ENGINE= InnoDB;UPDATEhit_counterSETcnt = cnt + 1WHEREslot = RAND() * 100;SELECTSUM(cnt)FROMhit_counter;
一个常见需要时每个一段时间开始一个新的计算器(例如,每天一个)。
DROPTABLEIFEXISTSdaily_hit_counter;CREATETABLEdaily_hit_counter (
day DATENOTNULL,
slot TINYINT UNSIGNEDNOTNULL,
cnt INT UNSIGNEDNOTNULL,PRIMARYKEY(day, slot)
)ENGINE= InnoDB;-- 插入数据INSERTINTOdaily_hit_counter (day, slot, cnt)VALUES(current_date, rand() * 100, 1)ONDUPLICATEKEYUPDATEcnt = cnt + 1;-- 定期执行:合并所有结果到 0 号槽,并且删除所有其他的槽:UPDATEdaily_hit_counterAScINNERJOIN(SELECTday,
sum(cnt)AScnt,
min(slot)ASmslotFROMdaily_hit_counterGROUPBYday
)ASxUSING(day)SETc.cnt =if(c.slot = x.mslot, x.cnt, 0),
c.slot =if(c.slot = x.mslot, 0, c.slot);DELETEFROMdaily_hit_counterWHEREslot <> 0ANDcnt = 0;
统计同一列的不同值的数量,以减少查询的语句量。可以这样:
DROPTABLEIFEXISTSitems;CREATETABLEitems (
id INTEGERAUTO_INCREMENTPRIMARYKEY,
color VARCHAR(50)
);SELECTSUM(IF(color = 'blue', 1, 0))ASblue,
SUM(IF(color = 'red', 1, 0))ASredFROMitems;SELECTCOUNT(color = 'blue'ORNULL)ASblue,
COUNT(color = 'red'ORNULL)ASredFROMitems;