「不當家不知柴米貴」,Schema 設計中資料型別的選擇直接決定儲存成本、索引大小、CPU 計算效率。本文深入剖析 INT(M) 顯示寬度迷思、VARCHAR(N) 最大長度計算、TIMESTAMP vs DATETIME 選型陷阱。
INT(M):M 只是顯示寬度,不影響儲存INT 永遠佔 4 位元組,有號範圍 -2147483648 ~ 2147483647。M (1~255) 僅影響 ZEROFILL 時的前導零填充顯示。CREATE TABLE t (id INT(2));
INSERT INTO t VALUES (10086); -- 正常寫入,顯示 10086
ALTER TABLE t MODIFY id INT(16) ZEROFILL;
SELECT * FROM t; -- 0000000000010086
ALTER TABLE t MODIFY id INT(5) ZEROFILL;
SELECT * FROM t; -- 10086 (寬度不足不截斷)
結論: 忽略 M,直接寫 INT 或 BIGINT;需要前導零才加 ZEROFILL(隱含 UNSIGNED)。
VARCHAR(N) 最大 N 計算實戰單行上限 65535 位元組(不含 BLOB/TEXT 溢位頁)。扣除開銷:
-- latin1 (1 byte/char)
CREATE TABLE t (c VARCHAR(65533) CHARSET latin1); -- OK (65533+1+1=65535)
CREATE TABLE t (c VARCHAR(65534) CHARSET latin1); -- ERROR 1118 Row size too large
-- utf8 (3 bytes/char)
-- (65535 - 1 - 2) / 3 = 21844
CREATE TABLE t (c VARCHAR(21844) CHARSET utf8); -- OK
CREATE TABLE t (col1 INT, col2 CHAR(50), col3 VARCHAR(N)) CHARSET=utf8; N 最大?行開銷:col1 4B + col2 50×3=150B + col3 長度前綴 2B + NULL 位圖 1B = 157B 已用。
剩餘:65535 - 157 = 65378 B → N = FLOOR(65378 / 3) = 21792。
TIMESTAMP 那些事DATETIME 8 位元組,DATE 8 位元組)。DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP 內建建立/更新時間戳。DATETIME 直接相減的坑CREATE TABLE t (start DATETIME, stop DATETIME);
INSERT INTO t VALUES ('2014-01-19 21:46:18', '2014-01-20 00:21:31');
SELECT stop - start FROM t; -- 787513 (數字相減,非時間差!)
正確做法:TIMESTAMPDIFF(SECOND, start, stop) 或 UNIX_TIMESTAMP(stop) - UNIX_TIMESTAMP(start)。
SHOW CREATE TABLE 驗證MySQL 5.6 前:TIMESTAMP 預設 NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(首欄自動加)。
5.6.6+:explicit_defaults_for_timestamp=ON 關閉隱式預設值。
最佳實踐: 顯式指定所有屬性,建表後用 SHOW CREATE TABLE 確認實際 DDL。
INT UNSIGNED 存 Unix 時間戳雖同為 4 位元組,但失去 FROM_UNIXTIME()/UNIX_TIMESTAMP() 函數支援、時區自動轉換、DATE_FORMAT 直讀等便利,維護成本更高。
TINYINT/SMALLINT/MEDIUMINT/INT/BIGINT → 依最大值選最小型別,UNSIGNED 翻倍正數範圍。CHAR(如 MD5、UUID、ISO 代碼)。VARCHAR(N),N 為業務實際上限,編碼 utf8mb4。TIMESTAMP (1970-2038) 或 DATETIME (1000-9999),需跨世紀用後者。DECIMAL(p,s)(金額、比率),禁止 FLOAT/DOUBLE。TEXT/BLOB 族,必要時外部儲存 (S3/MinIO) + 存 URL。嚴選型別、拒絕冗餘,是 Schema 設計的最高原則。