MariaDB講座 / 文字列型を選ぶ:CHAR・VARCHAR・TEXT

固定長コードにはCHAR、長さが変わる名前にはVARCHAR、長い説明にはTEXTを検討します。宣言した長さだけで判断せず、文字セット、バイト上限、末尾空白と比較規則も確認します。

型と入力仕様を組み合わせる

型 用途の候補 注意点
CHAR(M) 長さが一定のコード 不足分が埋められ、取得時に末尾空白が除かれる場合がある
VARCHAR(M) 名前・見出し・メールアドレス Mは文字数上限。行全体と文字コードの制限もある
TEXT 長い説明や本文 標準TEXTの容量は65,535バイトで文字数とは違う

CHARが常に高速とは限らず、保存エンジン、実データとアクセス方法によります。VARCHAR(100)も必ず100文字を要求する型ではありません。TEXT(M)は必要なTEXT系の型を選ぶ指定で、VARCHARのようにM文字で必ず拒否する規則ではありません。容量が必要ならMEDIUMTEXTやLONGTEXTも用途に応じて検討します。

文字数とバイト数を比べる

utf8mb4の接続で実行します。

SELECT CHAR_LENGTH('café') AS characters,
       LENGTH('café') AS bytes;

caféは4文字、5バイトです。合成済みのéがUTF-8で2バイトで、他の三文字は各1バイトだからです。CHAR_LENGTHはDBの文字数で、利用者が一文字と見る結合文字や絵文字を常に一つと数えるわけではありません。画面上の文字数制限には別のUnicode方針が必要な場合があります。

文字セットは表現、照合順序は比較・並べ替えの規則です。文字セットと照合順序、文字列関数と合わせて確認します。

同じ入力をCHARとVARCHARへ保存する

実習アカウントでshopcourseを確認します。

SELECT VERSION() AS server_version, DATABASE() AS selected_database;
USE shopcourse;
SELECT DATABASE() AS selected_database;

元のモードを保存し、PAD_CHAR_TO_FULL_LENGTHを含まないSTRICT_TRANS_TABLESで比較します。通常例全体を実行します。

USE shopcourse;
SET @saved_sql_mode = @@SESSION.sql_mode;
SET SESSION sql_mode = 'STRICT_TRANS_TABLES';
CREATE TABLE string_lab (
  item_id INT PRIMARY KEY,
  fixed_code CHAR(4) NOT NULL,
  title VARCHAR(4) NOT NULL,
  description TEXT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO string_lab VALUES
  (1, 'AB  ', 'AB  ', 'Product description'),
  (2, 'CD', 'é🙂', NULL);
SELECT item_id, CONCAT('[', fixed_code, ']') AS fixed_display,
       CHAR_LENGTH(fixed_code) AS fixed_chars,
       CONCAT('[', title, ']') AS title_display,
       CHAR_LENGTH(title) AS title_chars, LENGTH(title) AS title_bytes
FROM string_lab ORDER BY item_id;
SELECT CHAR_LENGTH('café') AS characters,
       LENGTH('café') AS bytes;

一行目のCHARは[AB]で長さ2、VARCHARは[AB ]で長さ4・4バイトになります。CONCATの括弧が空白を見分ける助けになります。二行目のé🙂は2文字・6バイトで、VARCHAR(4)に入ります。4をバイト上限だと解釈すると誤ります。

長さ超過を入力規則として判断する

同じstrictモードの接続で、失敗例だけを実行します。

INSERT INTO string_lab VALUES (3, 'EF', 'abcde', NULL);

abcdeは5文字なのでData too longとなり、件数は2のままです。名前や識別子を黙って切り詰めず、入力を拒否するか上限を変えるかを判断します。strictモードを外すと切り詰めと警告になる場合があります。超過分が空白だけの場合も同じ扱いとは限りません。

保存と比較、文字とバイナリーを分ける

VARCHARが末尾空白を保存しても、utf8mb4_unicode_ciによる比較が大文字小文字や空白の差をすべて区別するとは限りません。異なる表記を保存できることと、UNIQUEを両方が通ることは別です。一意制約で重複判定を確認できます。

長文を保存できるからといって短いコードにもTEXTを使うとは限りません。検索、並べ替え、インデックスの上限やプレフィックスを考えます。画像・圧縮ファイルはバイトデータなので、一般的な文章用TEXTとは別にバイナリー型や外部保存を検討します。NULLの説明と空文字も欠損値と既定値で区別します。

定義を確認し実習をやり直す

SHOW CREATE TABLE string_labで実際の型・文字セット・照合順序を読み、CHAR_LENGTHとLENGTHを同じものとして扱わないよう結果を確認します。保存した接続でモードを戻します。

SET SESSION sql_mode = @saved_sql_mode;

切断済みなら保存変数に依存せず、新接続の設定を確認します。再作成は、この表が削除してよい対象だと定義・行を確認し、必要なデータを保存した後だけ行います。

DROP TABLE shopcourse.string_lab;

通常のROLLBACKでは表を復元できません。他の表を巻き込まず、string_labだけで比較を繰り返します。

関連する公式リファレンス