- 設計
氏名・住所・電話番号・メールの桁数と型の決め方|項目設計一覧表
氏名・住所・電話番号・メールアドレスの桁数は、「規格で上限が決まっている項目」と「自分たちで決める項目」に分けると迷いません。メールアドレスは254文字、電話番号はE.164形式で最大15桁の数字を文字列型で、郵便番号はハイフンなし7桁の文字列で持つ、がこの記事の推奨値です。
この記事は、項目ごとの桁数と型を根拠つきの一覧表で示し、そのまま使える項目定義書(Markdownの表とタブ区切り)と、PostgreSQL・MySQLのCREATE TABLEを載せます。制約で止まるときのエラー文、文字数とバイト数の数え方の違いも、実際に流した結果で示します。メールアドレスの形式チェック(正規表現)は扱わず、桁数と型に絞ります。
確認環境:Ubuntu 24.04、PostgreSQL 16.15、MySQL 8.0.46、Python 3.11.15、Node.js 22(確認日 2026-10-06)。例に出てくる氏名・住所・電話番号・メールアドレスはすべて架空のものです。個人情報の取り扱いそのもの(取得・保管・開示のルール)は、自社の規程と法務の確認に従ってください。
この記事の結論
- メールアドレスは varchar(254)。 RFC 5321 の経路の上限256オクテットから、前後の山かっこ2文字を除いた値。@ より前は64オクテットまで。
- 電話番号は文字列型で、E.164形式(+ と国番号から始まる最大15桁の数字)を varchar(16) に入れる。 先頭のゼロや + が意味を持ち、計算もしないため、数値型には入れない。
- 郵便番号はハイフンなし7桁を char(7) で持つ。 数値型にすると 0600000 が 600000 になる。ハイフンは表示のときに付ける。
- 氏名・住所の桁数に規格上の上限は無い。 入力画面の上限を先に決め、DBはそれ以上の桁数にする(この記事の値は例)。
- 入力上限・DB桁数・表示桁数は別々に決める。 3つを同じ数字にそろえる必要はない。
項目別の推奨桁数と型 早見表
| 項目 | 型と桁数 | 保存する形 | 根拠 | 区分 |
|---|---|---|---|---|
| メールアドレス | varchar(254) | 入力のまま(前後の空白は除く) | RFC 5321 4.5.3.1.3(経路は区切り記号込みで最大256オクテット)、RFC 3696 正誤表 1690(上限は254) | 規格 |
| メールの @ より前 | (上の列の中で検査)64まで | ― | RFC 5321 4.5.3.1.1(local-part は最大64オクテット) | 規格 |
| 電話番号 | varchar(16) | +81300000000 のような E.164 形式 | ITU-T E.164(国際番号は国際プレフィックスを除いて最大15桁)+先頭の + 1文字 | 規格 |
| 郵便番号 | char(7) | 0600000(ハイフンなし) | 日本郵便の郵便番号データの説明(郵便番号は7桁) | 公開情報 |
| 都道府県 | varchar(4) | 北海道、神奈川県 | 47都道府県の名前の最長が4文字(Pythonで数えた) | 計算 |
| 姓・名 | varchar(50) ずつ | 入力のまま | 規格上の上限は無い | 例 |
| 姓・名のフリガナ | varchar(100) ずつ | 全角カタカナ | 規格上の上限は無い(漢字より文字数が増えるため長めに) | 例 |
| 市区町村 | varchar(50) | 見本市見本区 | 規格上の上限は無い | 例 |
| 町名・番地 | varchar(100) | 見本町1丁目2番3号 | 規格上の上限は無い(日本郵便のデータでは、町域名が全角38文字を超えると複数行に分かれる) | 例 |
| 建物名・部屋番号 | varchar(100) | サンプルビル101 | 規格上の上限は無い | 例 |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「氏名・住所・電話番号・メールの桁数と型の決め方|項目設計一覧表」
桁数の根拠を確かめる
メールアドレスは254文字
RFC 5321(SMTP)の 4.5.3.1 に、次の上限が書かれています。
| 対象 | 上限 | 節 |
|---|---|---|
| local-part(@ より前) | 64オクテット | 4.5.3.1.1 |
| ドメイン | 255オクテット | 4.5.3.1.2 |
| 経路(forward-path/reverse-path) | 256オクテット(区切り記号を含む) | 4.5.3.1.3 |
経路は山かっこで囲んだ形で送られるため、256から山かっこの2文字を引いた254が、アドレスそのものの上限になります。RFC 3696 の正誤表(Errata ID 1690、状態は Verified)でも、アドレス長の上限は254と訂正されています。64+1+255=320 という数字を見かけることがありますが、経路の上限が先に効くため、DBの桁数は254で足ります。
電話番号はE.164で最大15桁
ITU-T 勧告 E.164(The international public telecommunication numbering plan)は、国際的な電話番号の番号計画を定めた勧告です。ITUが公開している本文(05/97版)の 6.1 節に、国際番号の桁数は、国際プレフィックスを除いて最大15桁とする旨が書かれています。国番号の桁数を n とすると、国内番号は最大 15−n 桁です(7.2.1 節)。なお、ITUの勧告一覧ページでは、E.164 の現行版は 02/2026 版と表示されていました。
システムでは、この15桁の前に + を付けた形(例:+81300000000)で保存するのが一般的で、この記事でも「E.164形式」と呼びます。+ の1文字を足して varchar(16) です。
郵便番号は7桁
日本郵便が公開している郵便番号データの説明ページには、データの項目として「郵便番号(7桁)」が挙げられています。同じページに、町域名が全角38文字を超える場合は複数のレコードに分けて収録するという記載もあり、町名の列を短くしすぎないための目安になります。
数値型と文字列型の選び方
先頭のゼロに意味がある値、計算しない値は文字列型にします。
| 判断の問い | はい | いいえ |
|---|---|---|
| 足し算・平均などの計算をするか | 数値型 | 文字列型を検討 |
| 先頭のゼロに意味があるか | 文字列型 | ― |
| 数字以外の文字(+、-、英字)が入りうるか | 文字列型 | ― |
| 大小比較に意味があるか(金額・数量・年齢) | 数値型 | 文字列型 |
郵便番号と電話番号を数値型に入れた場合の結果です(PostgreSQL 16.15)。
SELECT '0600000'::integer AS postal_as_int,
'09000000000'::bigint AS phone_as_int,
lpad(600000::text, 7, '0') AS restored;
postal_as_int | phone_as_int | restored
---------------+--------------+----------
600000 | 9000000000 | 0600000
(1 row)
先頭のゼロが落ちます。 郵便番号は桁数が固定なので lpad で戻せますが、電話番号は桁数が番号によって違うため、元の形に戻せません。会員番号・社員番号・商品コードも同じ考え方で文字列型にします。
| 項目 | 型 | 理由 |
|---|---|---|
| 郵便番号 | 文字列(char(7)) | 先頭ゼロあり、計算しない |
| 電話番号 | 文字列(varchar(16)) | 先頭の + と国番号、計算しない |
| 番地・部屋番号 | 文字列 | 「1-2-3」「101号室」「B棟」など数字以外が入る |
| 年齢 | 持たない(生年月日を date 型で持つ) | 年齢は日がたつと変わる |
| 生年月日 | date | 日付の計算・範囲検索をする |
入力上限・DB桁数・表示桁数を分けて決める
桁数の話がかみ合わないときは、次の3つが混ざっていることが多いです。
| 種類 | 決める場所 | 決め方 | 超えたときの動き |
|---|---|---|---|
| 入力上限 | 画面項目定義・入力チェック | 利用者が入れる値として受け付ける最大の文字数 | 入力エラーにして、文言で上限を伝える |
| DB桁数 | テーブル定義 | 入力上限以上。規格の上限がある項目は規格に合わせる | DBがエラーを返す(最後の守り) |
| 表示桁数 | 一覧画面・帳票のレイアウト | 枠に収まる文字数。超える分は省略や折り返し | 「…」で省略、ツールチップや詳細画面で全文を出す |
決める順番と基準は次のとおりです。
- 規格のある項目は、DB桁数を規格の上限にする(メール254、電話16、郵便番号7)。入力上限も同じ値にする。
- 規格のない項目は、入力上限を先に決める(例:姓・名は各50文字)。帳票や連携先のシステムに桁数の制限があれば、それを入力上限に反映する。
- DB桁数は入力上限と同じか、それより大きくする。 小さくすると、入力チェックを通った値がDBで止まる。
- 表示桁数は入力上限より小さくてよい。 一覧では省略し、詳細画面で全文を出す。
- 外部へ出力するとき(CSV・帳票・連携)は、出力先の桁数で改めて検査する。 バイト数で制限されている連携先では、文字数ではなくバイト数で数える。
記入例(氏名の姓):
| 項目 | 入力上限 | DB桁数 | 一覧画面の表示桁数 | 帳票の表示桁数 |
|---|---|---|---|---|
| 姓 | 50文字 | varchar(50) | 10文字(超える分は「…」) | 20文字(超える分は縮小して印字) |
| メールアドレス | 254文字 | varchar(254) | 30文字(超える分は「…」) | 出力しない |
項目定義書(Markdownの表とタブ区切り)
会員情報を題材にした項目定義書です。区分が「例」の桁数は、自分のシステムの入力上限に合わせて変えてください。
| No | 項目名 | 物理名 | 型 | 桁数 | 必須 | 保存する形・制約 | 区分 |
|---|---|---|---|---|---|---|---|
| 1 | 姓 | last_name | varchar | 50 | ○ | 入力のまま。前後の空白を除く | 例 |
| 2 | 名 | first_name | varchar | 50 | ○ | 入力のまま。前後の空白を除く | 例 |
| 3 | 姓(フリガナ) | last_name_kana | varchar | 100 | ○ | 全角カタカナと長音のみ | 例 |
| 4 | 名(フリガナ) | first_name_kana | varchar | 100 | ○ | 全角カタカナと長音のみ | 例 |
| 5 | メールアドレス | varchar | 254 | ○ | 一意。@ より前は64まで | 規格(RFC 5321) | |
| 6 | 電話番号 | phone | varchar | 16 | ― | E.164形式(+ と1〜9で始まる最大15桁の数字) | 規格(ITU-T E.164) |
| 7 | 郵便番号 | postal_code | char | 7 | ― | 半角数字7桁、ハイフンなし | 公開情報(日本郵便) |
| 8 | 都道府県 | prefecture | varchar | 4 | ― | 47都道府県のいずれか | 計算(最長4文字) |
| 9 | 市区町村 | city | varchar | 50 | ― | 入力のまま | 例 |
| 10 | 町名・番地 | street | varchar | 100 | ― | 入力のまま | 例 |
| 11 | 建物名・部屋番号 | building | varchar | 100 | ― | 入力のまま | 例 |
表計算ソフトに貼り付けるためのタブ区切りです。
No 項目名 物理名 型 桁数 必須 保存する形・制約 区分
1 姓 last_name varchar 50 ○ 入力のまま。前後の空白を除く 例
2 名 first_name varchar 50 ○ 入力のまま。前後の空白を除く 例
3 姓(フリガナ) last_name_kana varchar 100 ○ 全角カタカナと長音のみ 例
4 名(フリガナ) first_name_kana varchar 100 ○ 全角カタカナと長音のみ 例
5 メールアドレス email varchar 254 ○ 一意。@ より前は64まで 規格(RFC 5321)
6 電話番号 phone varchar 16 ― E.164形式(+ と1〜9で始まる最大15桁の数字) 規格(ITU-T E.164)
7 郵便番号 postal_code char 7 ― 半角数字7桁、ハイフンなし 公開情報(日本郵便)
8 都道府県 prefecture varchar 4 ― 47都道府県のいずれか 計算(最長4文字)
9 市区町村 city varchar 50 ― 入力のまま 例
10 町名・番地 street varchar 100 ― 入力のまま 例
11 建物名・部屋番号 building varchar 100 ― 入力のまま 例
姓名を分ける理由、フリガナを持つ理由
| 設計の選択 | 分ける・持つ場合にできること | 1列にまとめる場合 |
|---|---|---|
| 姓と名を分ける | 「見本 様」のように姓だけを使った宛名、姓での並べ替え・検索 | 後から機械的に分けるのは難しい(姓と名の区切りは文字から判断できない) |
| フリガナを持つ | 五十音順の並べ替え、読みでの検索、電話応対での確認 | 漢字の文字コード順になり、五十音順に並ばない |
| 住所を4つに分ける | 都道府県・市区町村での集計、郵便番号からの自動入力、宛名の改行位置の調整 | 入力は楽だが、地域別の集計に文字列の切り出しが要る |
海外の氏名や住所も扱うシステムでは、姓・名の順番や住所の区切り方が国によって違います。その場合は「表示用の氏名」を1列(例:varchar(100))で別に持ち、住所は「住所1」「住所2」のような汎用の列にする形もよく使われます。
PostgreSQLのCREATE TABLEとCHECK制約
CREATE TABLE member (
member_id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
last_name varchar(50) NOT NULL,
first_name varchar(50) NOT NULL,
last_name_kana varchar(100) NOT NULL CHECK (last_name_kana ~ '^[ァ-ヶー]+$'),
first_name_kana varchar(100) NOT NULL CHECK (first_name_kana ~ '^[ァ-ヶー]+$'),
email varchar(254) NOT NULL UNIQUE
CHECK (octet_length(email) <= 254
AND position('@' in email) BETWEEN 2 AND 65),
phone varchar(16) CHECK (phone ~ '^\+[1-9][0-9]{1,14}$'),
postal_code char(7) CHECK (postal_code ~ '^[0-9]{7}$'),
prefecture varchar(4),
city varchar(50),
street varchar(100),
building varchar(100)
);
INSERT INTO member (last_name, first_name, last_name_kana, first_name_kana,
email, phone, postal_code, prefecture, city, street, building)
VALUES ('見本','太郎','ミホン','タロウ','taro.mihon@example.com','+81300000000',
'0600000','北海道','見本市見本区','見本町1丁目2番3号','サンプルビル101');
SELECT member_id, last_name, email, phone, postal_code FROM member;
member_id | last_name | email | phone | postal_code
-----------+-----------+------------------------+--------------+-------------
1 | 見本 | taro.mihon@example.com | +81300000000 | 0600000
(1 row)
email のCHECKは、形式の検証ではなく長さだけを見ています。position('@' in email) BETWEEN 2 AND 65 は「@ があり、その前が1〜64文字」という意味です。
制約で止まる値とエラー文
各項目に、形が合わない値を1つずつ入れた結果です(共通の列は省略して値だけ示します)。
| 入れた値 | 止めた仕組み | PostgreSQL 16.15 のエラー文 |
|---|---|---|
| フリガナに「みほん」(ひらがな) | CHECK | new row for relation "member" violates check constraint "member_last_name_kana_check" |
| 電話番号に「03-0000-0000」 | CHECK | new row for relation "member" violates check constraint "member_phone_check" |
| 電話番号に「+1234567890123456」(数字16桁) | 桁数 | value too long for type character varying(16) |
| 郵便番号に「060-0000」(8文字) | 桁数 | value too long for type character(7) |
| 郵便番号に「060000」(6桁) | CHECK | new row for relation "member" violates check constraint "member_postal_code_check" |
| メール254文字(64+@+189) | ― | INSERT 0 1(登録できる) |
| メール255文字(64+@+190) | 桁数 | value too long for type character varying(254) |
| メールの @ より前が65文字 | CHECK | new row for relation "member" violates check constraint "member_email_check" |
6桁の郵便番号は、char(7) が末尾を空白で埋めて7文字にするため桁数では止まらず、CHECK制約(数字7桁)で止まりました。固定長の値は、型の桁数とCHECK制約の両方で守ります。
MySQLのCREATE TABLEとCHECK制約
MySQL 8.0 では、正規表現のCHECKを REGEXP_LIKE で書きます。英数字と記号だけの列(メール・電話番号・郵便番号)は文字セットを ascii にすると、全角文字の混入も型で止められます。
CREATE TABLE member (
member_id bigint AUTO_INCREMENT PRIMARY KEY,
last_name varchar(50) NOT NULL,
first_name varchar(50) NOT NULL,
last_name_kana varchar(100) NOT NULL,
first_name_kana varchar(100) NOT NULL,
email varchar(254) CHARACTER SET ascii NOT NULL UNIQUE,
phone varchar(16) CHARACTER SET ascii,
postal_code char(7) CHARACTER SET ascii,
prefecture varchar(4),
city varchar(50),
street varchar(100),
building varchar(100),
CONSTRAINT member_last_kana_check CHECK (REGEXP_LIKE(last_name_kana, '^[ァ-ヶー]+$', 'c')),
CONSTRAINT member_first_kana_check CHECK (REGEXP_LIKE(first_name_kana, '^[ァ-ヶー]+$', 'c')),
CONSTRAINT member_email_check CHECK (LOCATE('@', email) BETWEEN 2 AND 65),
CONSTRAINT member_phone_check CHECK (REGEXP_LIKE(phone, '^\\+[1-9][0-9]{1,14}$')),
CONSTRAINT member_postal_check CHECK (REGEXP_LIKE(postal_code, '^[0-9]{7}$'))
) DEFAULT CHARSET = utf8mb4;
同じ行を登録した結果:
+-----------+-----------+------------------------+--------------+-------------+
| member_id | last_name | email | phone | postal_code |
+-----------+-----------+------------------------+--------------+-------------+
| 1 | 見本 | taro.mihon@example.com | +81300000000 | 0600000 |
+-----------+-----------+------------------------+--------------+-------------+
| 入れた値 | MySQL 8.0.46 のエラー文 |
|---|---|
| フリガナに「みほん」 | ERROR 3819 (HY000): Check constraint 'member_last_kana_check' is violated. |
| 電話番号に「03-0000-0000」 | ERROR 3819 (HY000): Check constraint 'member_phone_check' is violated. |
| 郵便番号に「060-0000」 | ERROR 1406 (22001): Data too long for column 'postal_code' at row 1 |
| 郵便番号に「060000」 | ERROR 3819 (HY000): Check constraint 'member_postal_check' is violated. |
| メール255文字 | ERROR 1406 (22001): Data too long for column 'email' at row 1 |
| メールに全角の英字 | ERROR 1366 (HY000): Incorrect string value: '\xEF\xBD\x88\xEF\xBD\x81...' for column 'email' at row 1 |
| 登録済みと大文字・小文字だけ違うメール(Taro.Mihon@Example.com) | ERROR 1062 (23000): Duplicate entry 'Taro.Mihon@Example.com' for key 'member.email' |
桁数を超えた値がエラーになるのは、sql_mode に STRICT_TRANS_TABLES が入っているためです(MySQL 8.0 の既定)。MySQLのリファレンスには、厳密モードが無効のときは値が切り詰められて警告になると書かれています。
メールアドレスの大文字・小文字と一意制約
同じ「Taro.Mihon@Example.com」を、PostgreSQLの member テーブルに入れると登録できました。
INSERT 0 1
PostgreSQLの UNIQUE は大文字・小文字を区別し、MySQLの ascii 列(既定の照合順序)は区別しない、という違いです。どちらに寄せるかを仕様として決めておきます。 大文字・小文字の違いを同じアドレスとして扱うなら、PostgreSQLでは小文字にそろえた値に一意インデックスを付けます。
CREATE UNIQUE INDEX member_email_lower ON member (lower(email));
すでに大文字・小文字違いの行がある状態でこの文を流すと、次のように止まります。インデックスは、データが入る前に作っておきます。
ERROR: could not create unique index "member_email_lower"
DETAIL: Key (lower(email::text))=(taro.mihon@example.com) is duplicated.
文字数とバイト数の違い(VARCHARの数え方)
PostgreSQLもMySQLも、varchar(n) の n は文字数です。 PostgreSQLのドキュメントには「n 文字(バイトではない)まで」と明記され、MySQLのリファレンスにも、VARCHAR の長さは文字数の上限と書かれています。違いが出るのは、長さを返す関数と、上限の決まり方です。
長さを返す関数
-- PostgreSQL
SELECT s, char_length(s) AS chars, octet_length(s) AS bytes, length(s) AS length_fn
FROM (VALUES ('abc'),('見本'),('ミホン'),('ミホン'),('𠮷田'),('葛󠄀城')) v(s);
s | chars | bytes | length_fn
--------+-------+-------+-----------
abc | 3 | 3 | 3
見本 | 2 | 6 | 2
ミホン | 3 | 9 | 3
ミホン | 3 | 9 | 3
𠮷田 | 2 | 7 | 2
葛󠄀城 | 3 | 10 | 3
(6 rows)
-- MySQL
SELECT s, CHAR_LENGTH(s) AS chars, LENGTH(s) AS length_fn
FROM (SELECT 'abc' s UNION ALL SELECT '見本' UNION ALL SELECT 'ミホン'
UNION ALL SELECT 'ミホン' UNION ALL SELECT '𠮷田' UNION ALL SELECT '葛󠄀城') v;
MySQL 8.0.46 の結果を表にまとめます。
| 値 | CHAR_LENGTH | LENGTH |
|---|---|---|
| abc | 3 | 3 |
| 見本 | 2 | 6 |
| ミホン | 3 | 9 |
| ミホン(半角カタカナ) | 3 | 9 |
| 𠮷田 | 2 | 7 |
| 葛󠄀城(異体字セレクタつき) | 3 | 10 |
| 知りたいもの | PostgreSQL | MySQL |
|---|---|---|
| 文字数 | char_length(s)、length(s) | CHAR_LENGTH(s) |
| バイト数 | octet_length(s) | LENGTH(s) |
同じ length という名前でも、PostgreSQLは文字数、MySQLはバイト数を返します。 桁数チェックのSQLを移すときは、ここを書き換えます。
見た目は2文字でも3文字と数えられる氏名
最後の「葛󠄀城」は、1文字目に異体字セレクタ(字形を指定するための、目に見えない符号)が付いた例です。画面では2文字に見えますが、DBは3文字と数えます。varchar(2) の列に入れた結果です。
CREATE TABLE len_test (v varchar(2));
INSERT INTO len_test VALUES ('見本'); -- 2文字
INSERT INTO len_test VALUES ('𠮷田'); -- 2文字(1文字目はUTF-8で4バイト)
INSERT INTO len_test VALUES ('葛󠄀城'); -- 見た目2文字、数え方では3文字
INSERT 0 1
INSERT 0 1
ERROR: value too long for type character varying(2)
MySQLでも同じ値で ERROR 1406 (22001): Data too long for column 'v' at row 1 になりました。氏名の桁数をぎりぎりにしない理由の1つです。入力チェック側の数え方がDBとそろっているかも、設計時に確かめておきます。たとえば「𠮷田」は、Python 3.11 の len() では2、JavaScript(Node.js 22)の length では3と数えられました。
MySQLでは上限がバイト数から決まる
varchar(n) の n は文字数ですが、MySQLでは行の大きさとインデックスの大きさがバイト数で制限されます。utf8mb4(1文字最大4バイト)で確かめた結果です。
| 文 | 結果 |
|---|---|
| CREATE TABLE big1 (v varchar(16383)) | 作成できる |
| CREATE TABLE big2 (v varchar(16384)) | ERROR 1074 (42000): Column length too big for column 'v' (max = 16383); use BLOB or TEXT instead |
| CREATE TABLE idx1 (v varchar(768) UNIQUE) | 作成できる |
| CREATE TABLE idx2 (v varchar(769) UNIQUE) | ERROR 1071 (42000): Specified key was too long; max key length is 3072 bytes |
メールアドレスの varchar(254) は、utf8mb4 でも 254×4=1,016バイトなので、一意インデックスの上限(3,072バイト)に収まります。
入力値を保存する形にそろえる(Python)
入力はハイフンつきや全角でも受け付け、保存する前に1つの形にそろえると、検索と重複判定が楽になります。日本国内の番号を前提にした最小の例です(国番号 +81 を付ける部分は、扱う国に合わせて変えます)。
import re
import unicodedata
def normalize_postal(text):
"""全角・ハイフンつきの入力を、ハイフンなし7桁の文字列にする"""
s = unicodedata.normalize("NFKC", text)
s = re.sub(r"[-‐−ー\s]", "", s)
if not re.fullmatch(r"[0-9]{7}", s):
raise ValueError(f"郵便番号は7桁の数字で入力してください: {text!r}")
return s
def format_postal(code):
"""表示用:3桁-4桁"""
return f"{code[:3]}-{code[3:]}"
def to_e164_jp(text):
"""日本の国内番号(0始まり)を E.164 形式にする"""
s = unicodedata.normalize("NFKC", text)
s = re.sub(r"[-‐−ー()\s]", "", s)
if s.startswith("+"):
result = s
elif s.startswith("0"):
result = "+81" + s[1:]
else:
raise ValueError(f"0 または + で始めてください: {text!r}")
if not re.fullmatch(r"\+[1-9][0-9]{1,14}", result):
raise ValueError(f"E.164 の形式(+ と最大15桁の数字)になりません: {text!r}")
return result
for raw in ["060-0000", "060-0000", "0600000", "060-000"]:
try:
code = normalize_postal(raw)
print(f"{raw!r:16} -> 保存 {code!r} / 表示 {format_postal(code)!r}")
except ValueError as e:
print(f"{raw!r:16} -> {e}")
for raw in ["03-0000-0000", "090-0000-0000", "090(0000)0000",
"+81 90 0000 0000", "+1234567890123456"]:
try:
num = to_e164_jp(raw)
print(f"{raw!r:28} -> {num!r}(+を除いて{len(num) - 1}桁)")
except ValueError as e:
print(f"{raw!r:28} -> {e}")
実行結果(Python 3.11.15):
'060-0000' -> 保存 '0600000' / 表示 '060-0000'
'060-0000' -> 保存 '0600000' / 表示 '060-0000'
'0600000' -> 保存 '0600000' / 表示 '060-0000'
'060-000' -> 郵便番号は7桁の数字で入力してください: '060-000'
'03-0000-0000' -> '+81300000000'(+を除いて11桁)
'090-0000-0000' -> '+819000000000'(+を除いて12桁)
'090(0000)0000' -> '+819000000000'(+を除いて12桁)
'+81 90 0000 0000' -> '+819000000000'(+を除いて12桁)
'+1234567890123456' -> E.164 の形式(+ と最大15桁の数字)になりません: '+1234567890123456'
似た決め方との違いと選び方
| 決め方 | 内容 | 向いている場面 |
|---|---|---|
| 規格の上限に合わせる | メール254、電話15桁+1、郵便番号7 | 規格のある項目すべて |
| 入力上限から決める | 画面で受け付ける文字数を先に決め、DBはそれ以上にする | 氏名・住所など規格のない項目 |
| 連携先の桁数に合わせる | 帳票、宛名印刷、外部システムの桁数に合わせて入力上限を決める | 出力先が決まっているシステム |
| すべて大きめにそろえる(varchar(255) や text) | 桁数の検討を省く | 試作段階。本番では入力上限と表示の検討が別に必要になる |
| 条件 | 選ぶもの |
|---|---|
| 規格で上限が決まっている | 規格の値をDB桁数と入力上限にする |
| 規格は無いが、出力先(帳票・連携)に桁数の制限がある | 出力先の桁数を入力上限にし、DBは同じかそれ以上 |
| 規格も出力先の制限も無い | 入力上限を例の値(姓・名50、住所100など)で決め、DBは同じ値 |
| 値の先頭にゼロや記号がある、計算しない | 文字列型 |
| 桁数が必ず同じ(郵便番号) | char(n)+CHECK制約 |
| 桁数が値によって違う(電話番号、メール) | varchar(n)+CHECK制約 |
確かめて分かったこと
- char(7) に6桁の郵便番号を入れても、桁数では止まらなかった。 末尾が空白で埋められて7文字になるため、数字7桁のCHECK制約が必要だった。
- 異体字セレクタつきの漢字は、見た目1文字でも2文字と数えられた。 varchar(2) の列に「見た目2文字」の氏名が入らなかった(PostgreSQL・MySQLとも)。
- length() の意味がDBで違った。 PostgreSQLは文字数、MySQLはバイト数を返した。
- メールアドレスの一意制約は、PostgreSQLでは大文字・小文字違いを別の値として登録でき、MySQLの ascii 列(既定の照合順序)では重複エラーになった。
- MySQLの utf8mb4 では、varchar の桁数は16,383まで、一意インデックスを付けられるのは768までだった(それぞれ1つ上の値でエラーになった)。
- 47都道府県の名前の最長は4文字(神奈川県・和歌山県・鹿児島県)だった。
よくある質問
Q. メールアドレスの最大長は254文字と320文字のどちらですか? A. 254文字です。64+1+255=320 は各部分の上限を足した数字で、RFC 5321 では経路全体が256オクテット(山かっこ込み)に制限されるため、アドレスは254までになります。
Q. 電話番号を数値型(INT/BIGINT)で持てないのはなぜですか? A. 先頭のゼロや + が落ちて、元の番号に戻せなくなるためです。電話番号は計算に使わない値なので、文字列型で持ちます。
Q. 郵便番号はハイフンありとなしのどちらで保存しますか? A. ハイフンなしの7桁で保存します。保存する形を1つにしておくと検索と重複判定がそのまま書け、ハイフンは表示のときに付けられます。
Q. VARCHAR(50) は50バイトですか、50文字ですか? A. PostgreSQLとMySQL 8.0では50文字です。日本語でも50文字まで入ります。バイト数で数える製品や設定もあるため、使うDBのドキュメントで確かめてください。
Q. 氏名の桁数は何文字にすればよいですか? A. 規格上の上限は無いので、入力画面で受け付ける文字数を先に決め、DBはそれ以上にします。この記事では姓・名それぞれ50文字を例にしています。
まとめ
- メールアドレスは varchar(254)(RFC 5321 の経路256オクテットから山かっこ2文字を除く)。@ より前は64まで。
- 電話番号は E.164 形式(+ と最大15桁の数字)を varchar(16) で持つ。数値型にはしない。
- 郵便番号はハイフンなし7桁を char(7)+CHECK制約で持ち、表示のときにハイフンを付ける。
- 氏名・住所は規格上の上限が無い。 入力上限を先に決め、DB桁数はそれ以上にする。
- 入力上限・DB桁数・表示桁数は別のものとして、項目定義書に分けて書く。
- varchar(n) の n は文字数(PostgreSQL・MySQL)。ただし length() の意味、行やインデックスの上限はDBで違う。
- 入力値は保存前に1つの形にそろえる(全角→半角、ハイフン除去、E.164形式)。
参考・出典
- RFC 5321 Simple Mail Transfer Protocol(4.5.3.1 Size Limits and Minimums): https://www.rfc-editor.org/rfc/rfc5321.txt
- RFC Errata ID 1690(RFC 3696、アドレス長の上限254): https://www.rfc-editor.org/errata/eid1690
- ITU-T Recommendation E.164(05/97版の本文): https://www.itu.int/ITU-T/worksem/ip-telecoms/e164/e164.pdf
- ITU-T E.164 の版の一覧: https://www.itu.int/ITU-T/recommendations/rec.aspx?rec=10688
- 日本郵便「郵便番号データの説明」: https://www.post.japanpost.jp/zipcode/dl/readme.html
- PostgreSQL 16 文書「8.3. Character Types」: https://www.postgresql.org/docs/16/datatype-character.html
- MySQL 8.0 リファレンス「The CHAR and VARCHAR Types」: https://dev.mysql.com/doc/refman/8.0/en/char.html
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「氏名・住所・電話番号・メールの桁数と型の決め方|項目設計一覧表」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。