住所の正規化とは、同じ住所の書き方のばらつき(表記ゆれ)を、決まった1つの形にそろえることです。郵便番号・電話番号・ふりがなにも同じ考え方を使い、まとめてデータ正規化と呼びます。「123-4567」「〒123-4567」「1234567」を、すべて「1234567」にする作業がその例です。
寄付者のデータは、寄付決済サービス、クラウドファンディング、ネット募金、物品の寄付、自団体のフォームなど、多くの入口から届きます。入口ごとに住所や電話番号の書き方が違うため、そのままCRMに入れると、郵送物の宛名ラベルが崩れる、同じ人が別人として登録される、といったことが起きます。
この記事では、寄付で活動する非営利法人のZoho CRMで、当社が取込前の正規化ルールを設計したときの考え方をまとめます。年1回の年次報告書の郵送という期日があり、そこから逆算してルールを決めました。
なぜ取込の「前」にルールを決めるのか?
仕様を整理した時点で、寄付者データには次の3つの困りごとがありました。
- 郵送とメール配信がうまくいかない。 宛名ラベル用に書き出すと、郵便番号にハイフンがあるものとないものが混ざり、住所の欄も入口ごとに分かれ方が違いました
- 重複を見つけにくい。 同じメールアドレスでも全角と半角が混ざっていると、別の値として扱われます
- 運用の手間が増える。 書き方がそろっていないと、データを使うたびに確認や手直しが必要になります
どれも、データが入ってから直そうとすると手間が増えます。取り込む前に「この項目はこの形にする」と決め、取込のたびに同じ規則で直る仕組みにしておくのが近道です。
ルールを決めるときの出発点は、データを何に使うかです。この案件では、年次報告書のラベルに「氏名・郵便番号・住所・寄付の種類」が出せれば十分でした。使い道から必要な形を決め、それ以上は細かくしない、という順で考えます。
ルール1:住所は何項目で持つ?3項目にする

元のCRMでは、住所が6つの項目に分かれていました。これを次の3項目にまとめます。
| 項目 | 入れるもの | 例 |
|---|---|---|
| 郵便番号 | 記号を除いた半角7桁 | 1500001 |
| 都道府県 | そのまま | 東京都 |
| 市区町村以下 | 市区町村・町名・番地・建物名をつなげたもの | 渋谷区神宮前1-2-3 〇〇ビル101 |
3項目にした理由は2つあります。
- 元データが分けられていない。 寄付サービスから届く住所は、「都道府県以降」が1つの欄に入っていることが多く、町名と番地、番地と建物名の境目を機械的に見分けることはできません。細かい項目を用意しても埋まらず、空欄か分け間違いが増えるだけです
- 使い道に足りる。 宛名ラベルに必要なのは郵便番号・都道府県・それ以下の住所です
建物名を分けられないデータの扱いも論点になりました。手で事前に分ける案もありましたが、仕様では建物名も市区町村以下に含めることにしました。
市区町村以下をつなげるときの細かい決めごとは次のとおりです。
- 空の項目は飛ばしてつなぐ
- 番地と建物名の間は半角空白1つで区切る(当社の推奨)
- 建物名の中にある空白はそのまま残す
- 番地の数字は半角、区切りは半角ハイフン(-)にそろえる
- 都道府県が空で届く取込元では、郵便番号から都道府県を補う
区切りのハイフンをそろえるとき、長音の「ー」は置き換えません。「センター」のような建物名まで「セン-タ-」になってしまうためです。置き換えるのは「−」「‐」「-」のようなハイフンに似た記号だけにします。
ルール2:郵便番号は半角7桁
- 全角数字を半角にする
- ハイフン・空白・「〒」などの記号を除く
- 7桁にならなければ、元の値を残す
最後の1行が大事です。6桁や8桁のものを、桁を足したり削ったりして7桁にすると、見た目は正しくても別の地域の番号になるおそれがあります。直せないものは直さず、記録して人が確認します(後述)。
郵便番号から住所を自動で入れる仕組みは、「Zoho CRMで郵便番号から住所を自動入力する」で紹介しています。
ルール3:電話番号はハイフンなしの10桁か11桁
- 全角数字を半角にする
- ハイフン・空白・括弧を除く
- 先頭の「+81」を「0」に置き換える(海外の書き方で届く場合)
- 10桁か11桁にならなければ、元の値を残す
もともとのデータでは、市外局番が「03」のものは2桁、それ以外は3桁で区切る、という書き方が混ざっていました。区切りの位置は番号によって違うため、ハイフンは入れずに数字だけで持つほうが、比べるのも検索するのも簡単です。
ルール4:ふりがなは全角カタカナで1項目
- 「セイ」「メイ」を全角カタカナにそろえる(半角カタカナ・ひらがなを直す)
- セイとメイをつなげて「ふりがな」1項目にも入れる
取込元によっては半角カタカナで届き、手入力ではひらがなも混ざります。どれも全角カタカナに直します。半角の「ガ」は2文字(「カ」と濁点)なので、1文字ずつ直す前に「ガ→ガ」のように2文字の組を先に直すのがこつです。
セイとメイの間に空白を入れるかどうかは、どちらかに決めてそろえます。並べ替えや検索に使うなら空白なし、宛名で読みやすくするなら空白あり、と使い道で決めます。
ルール5:項目の種類ごとに全角・半角を決める
| 種類 | そろえ方 | 対象の例 |
|---|---|---|
| 数字 | 半角 | 郵便番号・電話番号・金額・日付 |
| カタカナ | 全角 | セイ・メイ・法人名のフリガナ |
| 英数字 | 半角 | メールアドレス・寄付サービスの会員番号 |
| 氏名の区切り | 全角空白 | 姓と名の間 |
| 住所の空白 | 不要なものを除く | 前後の空白など |
「メールアドレスの全角を半角に直す」は地味ですが効きます。全角の「@」や「.」が混ざったアドレスは、メール配信で届かないだけでなく、重複チェックでも別の人として扱われます。
ルール6:氏名が1つの欄で届くときの分け方
取込元によっては、姓と名が1つの欄に入って届きます。この場合は次の順で分けます。
- 空白(半角・全角どちらでも)があれば、その位置で分ける。1つ目を姓、残りを名にする
- 空白がなく3文字以上なら、先頭2文字を姓、残りを名にする
- 2文字以下なら、全体を姓にして名は空にする
(姓と名から氏名を組み立てる逆向きの処理は「Zoho CRMで氏名を姓・名から自動入力する構成案」で扱っています)
2の規則は割り切りです。名字が1文字や3文字の人(例:「佐々木」)は正しく分かれません。仕様では、分け間違いは月1回の点検で人が直す運用にしました。宛名ラベルで姓と名を続けて印字するなら、分け間違いの実害は比較的小さいと当社は考えています。どこまでを機械に任せ、どこから人が直すかを先に決めておくと、規則を複雑にしすぎずに済みます。
ルール7:匿名・募金箱のデータは決まった名前で受ける
ネット募金などでは、寄付者が匿名を選べます。氏名の欄が空、または「匿名」「-」で届いたものは、「(取込元の名前)匿名」という決まった姓で登録し、名は空にします。取込元ごとに分けておけば、どこからの匿名寄付かが後から集計できます。
募金箱のように個人の情報がないものは、姓を「募金箱」、ふりがなを「ボキンバコ」と固定します。寄付の合計額には含めつつ、寄付者数を数える集計(認定NPO法人のパブリック・サポート・テストなど)を行う場合はその対象から外せるよう、対象外の印を付けておきます。
ルール8:どの住所をどこに持つか
同じ人でも、自宅・勤務先・郵送物の送り先と、住所がいくつもあります。この案件では次のように決めました。
- 連絡先には、郵送物の送り先の住所だけを持つ(ラベル印刷に使う)
- 取引先(法人)には、法人の住所を持つ
- 寄付の記録には、寄付したときの住所を履歴として残す(寄付者名簿に使う)
送り先の住所が指定されていればそれを優先し、なければ通常の住所を送り先とします。新しい寄付データが届いたときは、既存の「情報更新日」と寄付日を比べ、寄付日のほうが新しい場合だけ連絡先の住所を書き換えます。古い寄付データを後から取り込んだときに、新しい住所が古い住所で上書きされるのを防ぐためです。
直せない値は「元のまま残して記録する」
ここまでのルールで何度か出てきたとおり、決まった形にならない値は直しません。代わりに、次の項目を持つ記録用の表(カスタムモジュール)に1行ずつ残します。
| 項目 | 内容 |
|---|---|
| 発生日時 | 処理した日時 |
| モジュール名・データID | どのデータか |
| エラーの種類 | 郵便番号・電話番号・住所・氏名の分割 など |
| 元の値 | 直せなかった値 |
| 対応状況 | 未対応・対応中・対応済み |
月に1回、「未対応」を種類ごとに見て、元データを確かめて手で直します。同じ種類が増えていれば、ルールの追加を検討します。直せないデータが目に見える形で残ることが、ルールを育てる材料になります。
正規化はDataPrepとCRMの関数のどちらで行う?
| 置き場所 | 担当する処理 | 理由 |
|---|---|---|
| CRMのワークフロー+関数 | 郵便番号・電話・ふりがな・全角半角・住所の結合 | 手入力・フォーム・取込のどれから入っても同じ規則で直る |
| 取込前の加工(Zoho DataPrep) | 氏名の分け方、匿名の書き方、都道府県の補完など取込元ごとの癖 | 取込元によって規則が違う |
CRMの関数は、データの作成時と、郵便番号・電話番号・住所・氏名の項目が変わったときに動かします。作成時に重複チェックも行うなら、正規化と同じ関数の続きで行います。別々のワークフローに分けると、どちらが先に動くかを決められず、正規化の前の値で比べてしまうおそれがあるためです。DataPrepの基本的な使い方は「Zoho DataPrepでデータをクレンジング」で紹介しています。
Delugeのコード例
連絡先の作成時・編集時にワークフローから呼ぶ関数の例です。項目のAPI名は一般的な名前に置き換えています。お使いのCRMの項目名に合わせて変えてください。関数の1行目(関数名と引数)は、関数を作る画面で引数(ここでは連絡先のIDを文字列で受け取る contactId)を設定すると入ります。ワークフローから呼ぶときは、引数に連絡先のIDを割り当てます。直せなかった値は、記録用の複数行テキスト項目(Normalize_Note)と確認用のチェックボックス(Needs_Review)に残します。
void automation.NormalizeContact(string contactId)
{
rec = zoho.crm.getRecordById("Contacts",contactId.toLong());
if(rec.get("id") == null)
{
info "連絡先が取得できませんでした: " + contactId;
return;
}
upd = Map();
notes = List();
// ---- 変換表 ----
zenNum = "0,1,2,3,4,5,6,7,8,9".toList(",");
zenAlpha = "A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,@,.,_,+,-".toList(",");
hanAlpha = "A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,@,.,_,+,-".toList(",");
// 半角カナ→全角カナ(濁点・半濁点の2文字の組を先に並べる)
hanKana = "ガ,ギ,グ,ゲ,ゴ,ザ,ジ,ズ,ゼ,ゾ,ダ,ヂ,ヅ,デ,ド,バ,ビ,ブ,ベ,ボ,ヴ,パ,ピ,プ,ペ,ポ,ヲ,ァ,ィ,ゥ,ェ,ォ,ャ,ュ,ョ,ッ,ー,ア,イ,ウ,エ,オ,カ,キ,ク,ケ,コ,サ,シ,ス,セ,ソ,タ,チ,ツ,テ,ト,ナ,ニ,ヌ,ネ,ノ,ハ,ヒ,フ,ヘ,ホ,マ,ミ,ム,メ,モ,ヤ,ユ,ヨ,ラ,リ,ル,レ,ロ,ワ,ン".toList(",");
zenKana = "ガ,ギ,グ,ゲ,ゴ,ザ,ジ,ズ,ゼ,ゾ,ダ,ヂ,ヅ,デ,ド,バ,ビ,ブ,ベ,ボ,ヴ,パ,ピ,プ,ペ,ポ,ヲ,ァ,ィ,ゥ,ェ,ォ,ャ,ュ,ョ,ッ,ー,ア,イ,ウ,エ,オ,カ,キ,ク,ケ,コ,サ,シ,ス,セ,ソ,タ,チ,ツ,テ,ト,ナ,ニ,ヌ,ネ,ノ,ハ,ヒ,フ,ヘ,ホ,マ,ミ,ム,メ,モ,ヤ,ユ,ヨ,ラ,リ,ル,レ,ロ,ワ,ン".toList(",");
hira = "ぁ,あ,ぃ,い,ぅ,う,ぇ,え,ぉ,お,か,が,き,ぎ,く,ぐ,け,げ,こ,ご,さ,ざ,し,じ,す,ず,せ,ぜ,そ,ぞ,た,だ,ち,ぢ,っ,つ,づ,て,で,と,ど,な,に,ぬ,ね,の,は,ば,ぱ,ひ,び,ぴ,ふ,ぶ,ぷ,へ,べ,ぺ,ほ,ぼ,ぽ,ま,み,む,め,も,ゃ,や,ゅ,ゆ,ょ,よ,ら,り,る,れ,ろ,ゎ,わ,を,ん".toList(",");
kata = "ァ,ア,ィ,イ,ゥ,ウ,ェ,エ,ォ,オ,カ,ガ,キ,ギ,ク,グ,ケ,ゲ,コ,ゴ,サ,ザ,シ,ジ,ス,ズ,セ,ゼ,ソ,ゾ,タ,ダ,チ,ヂ,ッ,ツ,ヅ,テ,デ,ト,ド,ナ,ニ,ヌ,ネ,ノ,ハ,バ,パ,ヒ,ビ,ピ,フ,ブ,プ,ヘ,ベ,ペ,ホ,ボ,ポ,マ,ミ,ム,メ,モ,ャ,ヤ,ュ,ユ,ョ,ヨ,ラ,リ,ル,レ,ロ,ヮ,ワ,ヲ,ン".toList(",");
// セイとメイの区切り。空白ありにするなら " "
KANA_SEP = "";
// ---- 1. 郵便番号:半角7桁。ならなければ元の値を残す ----
zipRaw = ifnull(rec.get("Zip_Code"),"").toString().trim();
if(zipRaw != "")
{
zip = zipRaw;
for each index i in zenNum
{
zip = zip.replaceAll(zenNum.get(i),i.toString());
}
zip = zip.replaceAll("[^0-9]","");
if(zip.length() == 7)
{
if(zip != zipRaw)
{
upd.put("Zip_Code",zip);
}
}
else
{
notes.add("郵便番号が7桁になりません: " + zipRaw);
}
}
// ---- 2. 電話番号:+81を0に、ハイフンなしの10桁か11桁 ----
telRaw = ifnull(rec.get("Phone"),"").toString().trim();
if(telRaw != "")
{
tel = telRaw;
for each index i in zenNum
{
tel = tel.replaceAll(zenNum.get(i),i.toString());
}
tel = tel.replaceAll("+","+");
tel = tel.replaceAll("[^0-9+]","");
if(tel.startsWith("+810"))
{
// 「+81(0)3-…」の書き方
tel = "0" + tel.subString(4,tel.length());
}
else if(tel.startsWith("+81"))
{
tel = "0" + tel.subString(3,tel.length());
}
tel = tel.replaceAll("[^0-9]","");
if(tel.length() == 10 || tel.length() == 11)
{
if(tel != telRaw)
{
upd.put("Phone",tel);
}
}
else
{
notes.add("電話番号が10桁・11桁になりません: " + telRaw);
}
}
// ---- 3. メールアドレス:全角英数字・記号を半角に ----
mailRaw = ifnull(rec.get("Email"),"").toString().trim();
if(mailRaw != "")
{
mail = mailRaw;
for each index i in zenAlpha
{
mail = mail.replaceAll(zenAlpha.get(i),hanAlpha.get(i));
}
for each index i in zenNum
{
mail = mail.replaceAll(zenNum.get(i),i.toString());
}
mail = mail.toLowerCase();
if(mail != mailRaw)
{
upd.put("Email",mail);
}
}
// ---- 4. ふりがな:全角カタカナにそろえ、セイ+メイを1項目に ----
kanaFields = {"Last_Name_Kana","First_Name_Kana"};
kanaValues = List();
for each f in kanaFields
{
raw = ifnull(rec.get(f),"").toString().trim();
v = raw;
for each index i in hanKana
{
v = v.replaceAll(hanKana.get(i),zenKana.get(i));
}
for each index i in hira
{
v = v.replaceAll(hira.get(i),kata.get(i));
}
if(v != raw)
{
upd.put(f,v);
}
kanaValues.add(v);
}
furigana = kanaValues.get(0);
if(kanaValues.get(0) != "" && kanaValues.get(1) != "")
{
furigana = kanaValues.get(0) + KANA_SEP + kanaValues.get(1);
}
else if(kanaValues.get(0) == "")
{
furigana = kanaValues.get(1);
}
// セイ・メイが両方空のときは、既存のふりがなを空で上書きしない
if(furigana != "" && furigana != ifnull(rec.get("Furigana"),"").toString())
{
upd.put("Furigana",furigana);
}
// ---- 5. 市区町村以下:空の項目を飛ばしてつなぎ、数字・英字・ハイフンを半角に ----
parts = {"City","Town","Banchi"};
addr = "";
for each p in parts
{
addr = addr + ifnull(rec.get(p),"").toString().trim();
}
// 建物名は半角空白1つで区切って後ろにつなぐ(建物名の中の空白はそのまま)
building = ifnull(rec.get("Building"),"").toString().trim();
if(building != "")
{
if(addr != "")
{
addr = addr + " ";
}
addr = addr + building;
}
if(addr != "")
{
for each index i in zenNum
{
addr = addr.replaceAll(zenNum.get(i),i.toString());
}
for each index i in zenAlpha
{
addr = addr.replaceAll(zenAlpha.get(i),hanAlpha.get(i));
}
// ハイフンに似た記号だけを置き換える(長音「ー」は残す)
addr = addr.replaceAll("[−‐-―]","-");
if(addr != ifnull(rec.get("Address_Below"),"").toString())
{
upd.put("Address_Below",addr);
}
}
// ---- 6. 直せなかった値を残す ----
if(notes.size() > 0)
{
upd.put("Needs_Review",true);
upd.put("Normalize_Note",zoho.currenttime.toString("yyyy-MM-dd HH:mm") + " " + notes.toString("\n"));
}
// ---- 7. 変わった項目だけ更新する ----
if(upd.size() > 0)
{
opt = Map();
opt.put("trigger",List());
res = zoho.crm.updateRecord("Contacts",contactId.toLong(),upd,opt);
info res;
}
}
書くときのポイントは3つです。
- 変わった項目だけ更新します。 値が同じなら更新しないので、関数の更新がきっかけでワークフローが繰り返し動くのを防げます。更新時に
triggerを空にしているのも同じ目的です - 直せない値は上書きしません。
notesに理由を積み、確認用の項目に残すだけにしています。記録用のカスタムモジュールに1行ずつ作る形にしてもかまいません - 住所の結合は、元の細かい項目が残っている間の移行用です。 新しい取込で「市区町村以下」に直接入るようになったら、5の処理は外せます
既存データを一括で直す手順
新しい取込に規則を効かせる前に、すでに入っているデータを直します。
- 連絡先・見込み客・取引先と、寄付・申込のカスタムモジュールを、すべてCSVで書き出して日付付きで保管する
- 書き出した件数と、CRM上の件数が合っているかを確かめる
- サンドボックス(本番と切り離した検証用の環境)で、10件ほどに一括処理を試す
- 直った値と、記録に残った「直せない値」を目で確かめる
- 本番では、モジュールごとに200件ずつ区切って処理し、区切りごとに記録を見る。おかしな結果が出たらそこで止める
- 最後に、無作為に選んだデータを開いて確かめる
1のバックアップを取らずに始めないことが一番大切です。正規化は元の値を書き換える処理なので、規則の誤りに後から気づいたとき、戻せる元データが必要になります。
テストケースの例
入力と期待する結果を表にしておくと、規則を変えたときに同じ表で確かめ直せます。
| 項目 | 入力 | 期待する結果 |
|---|---|---|
| 郵便番号 | 123-4567 | 1234567 |
| 郵便番号 | 〒100-0001 | 1000001 |
| 郵便番号 | 123456(6桁) | 123456(元のまま・記録に残る) |
| 電話番号 | 03−1234−5678 | 0312345678 |
| 電話番号 | +81-3-9876-5432 | 0398765432 |
| 電話番号 | 090-1234(桁不足) | 090-1234(元のまま・記録に残る) |
| メール | test@example.com | test@example.com |
| セイ・メイ | ヤマダ/タロウ | ヤマダ/タロウ |
| セイ・メイ | たなか/はなこ | タナカ/ハナコ |
| 住所 | 渋谷区1−2−3 | 渋谷区1-2-3 |
| 住所 | 5F A−101 | 5F A-101 |
| 氏名(1つの欄) | 山田 太郎 | 姓:山田/名:太郎 |
| 氏名(1つの欄) | 山 | 姓:山/名:空 |
ハイフンに似た記号は「−」「‐」「-」「-」の4種類と、長音の「ー」「ー」をそれぞれ入れて、ハイフンだけが置き換わり長音が残ることも確かめます。
取込前のチェックリスト
- データの使い道(ラベル・メール・名簿など)と、それに必要な項目を書き出した
- 住所を何項目で持つかを決めた(元データが分けられているかを見て決める)
- 郵便番号・電話番号の桁と、ならなかったときの扱い(元のまま残す)を決めた
- ふりがなの文字種と、セイ・メイの区切りの有無を決めた
- 項目の種類ごとに全角・半角を決めた
- 氏名が1つの欄で届く取込元の分け方と、分け間違いを誰が直すかを決めた
- 匿名・募金箱など、個人の情報がないデータの登録の仕方を決めた
- 連絡先・取引先・寄付の記録に、どの住所を持つかを決めた
- 直せない値を残す場所と、確認する頻度を決めた
- 処理をCRMの関数と取込前の加工のどちらに置くかを、項目ごとに分けた
- 一括修正の前にバックアップを取り、件数を確かめた
ルールが決まれば、次は重複の扱いを決めます。そろえた値を使ってどう重複を見つけ、どこまでを自動にするかを考える段階です。名寄せの進め方の別の例は「顧客データ統合の進め方」で紹介しています。データの表記ルールの整備は、Zoho導入支援・コンサルティングでご相談いただけます。
よくある質問
住所は都道府県・市区町村・町名・番地・建物名と細かく分けたほうが便利ではありませんか?
分けた項目が埋まるなら便利です。ただ、寄付サービスから届く住所は1つの欄にまとめて入っていることが多く、機械的には正しく分けられません。宛名ラベルに必要なのは郵便番号・都道府県・それ以下の住所なので、3項目にそろえるほうが、空欄や分け間違いが起きにくくなります。
郵便番号が7桁にならないデータは、どう扱えばよいですか?
記号や全角数字を除いても7桁にならない場合は、元の値をそのまま残します。そのうえで、どの項目がなぜ直せなかったかを記録し、担当者が元データを見て直します。桁を補ったり削ったりして自動で直すと、誤った住所に郵送物が届くおそれがあります。
ふりがなはひらがなとカタカナのどちらにそろえるべきですか?
どちらでも構いませんが、1つに決めることが大切です。この記事の例では全角カタカナにそろえ、半角カタカナやひらがなで届いたものもカタカナへ直します。
正規化はZoho DataPrepとCRMの関数のどちらで行うべきですか?
両方に役割を分けます。郵便番号・電話・ふりがなのように、どの入口から入っても同じ規則で直せるものはCRMの関数に置きます。氏名が1つの欄で届く、匿名の書き方が独特など、取込元ごとの癖は取込前の加工(DataPrep)で直します。




