ลูกค้ากรอกที่อยู่ถูก แต่ขนส่งไม่รับ
อย่างตอนที่เราทำระบบหลังบ้านให้ร้านค้าออนไลน์ที่ส่งของผ่าน GoShip ช่องกรอกที่อยู่เป็นแบบเลือกจังหวัด อำเภอ ตำบล แล้วระบบเติมรหัสไปรษณีย์ให้เอง ข้อมูลชุดนี้เราโหลดมาจากชุดข้อมูลที่อยู่ที่ใช้กันทั่วไปบน GitHub
ใช้ไปสักพักก็เจอเคสที่ลูกค้าบอกว่ารหัสไปรษณีย์บ้านตัวเองไม่มีให้เลือก อย่างบางตำบลในอำเภอปากช่อง ไปรษณีย์ไทยแยกไปใช้รหัส 30450 แล้ว แต่ชุดข้อมูลของเรายังมีแค่ 30130 ส่วนขนส่งรับทั้งสองรหัส อีกทางหนึ่งก็มีรหัสที่ชุดข้อมูลของเราให้ แต่ขนส่งไม่รู้จัก
พอนั่งไล่ดูว่าเพราะอะไร ก็เจอว่าไม่ได้มีใครผิดอยู่คนเดียว ที่อยู่ไทยมีสองระบบซ้อนกันอยู่ และแต่ละรายการก็ตามการเปลี่ยนแปลงไม่ทันกันคนละจังหวะ
รหัสจังหวัด อำเภอ ตำบล เป็นของกรมการปกครอง
กรมการปกครองให้รหัสพื้นที่เป็นเลขสองหลักต่อกันทีละระดับ
| ระดับ | รหัส | ตัวอย่าง |
|---|---|---|
| จังหวัด | 2 หลัก | 10 กรุงเทพมหานคร |
| อำเภอ / เขต | 4 หลัก | 1001 เขตพระนคร |
| ตำบล / แขวง | 6 หลัก | 100101 แขวงพระบรมมหาราชวัง |
ทั้งประเทศมี 77 จังหวัด 928 อำเภอและเขต (อำเภอ 878 รวมกับ 50 เขตของกรุงเทพฯ) และราว 7,400 ตำบลและแขวง รหัส 6 หลักของตำบลบอกได้ทันทีว่าอยู่อำเภอไหน จังหวัดไหน ไม่ต้องเทียบชื่อ ซึ่งสำคัญมาก เพราะชื่อตำบลซ้ำกันข้ามจังหวัดเยอะ อย่าง ต.เกาะเต่า มีทั้งที่สุราษฎร์ธานีและพัทลุง
รายการทางการอยู่ที่หน้าทำเนียบท้องที่ของกรมการปกครอง เป็นไฟล์ ccaatt.xlsx มีรหัสกับชื่อทั้งภาษาไทยและอังกฤษ รหัสไหนยกเลิกแล้วจะมีเครื่องหมาย * พร้อมวันที่ยกเลิก แต่ไม่มีรหัสไปรษณีย์ หน้าเว็บเขียนว่าอัปเดต 08/07/2560 ขณะที่ในไฟล์บอกว่าเป็นข้อมูล ณ วันที่ 1 กันยายน 2566 จะรู้ว่าข้อมูลใหม่แค่ไหนจึงต้องเปิดไฟล์ดูเอง
รหัสไปรษณีย์แบ่งตามที่ทำการที่นำจ่าย
รหัสไปรษณีย์ 5 หลักเป็นของไปรษณีย์ไทย สองหลักแรกบอกจังหวัดทางไปรษณีย์ สามหลักหลังบอกที่ทำการที่นำจ่าย ส่วนใหญ่ลงท้ายด้วย 000 สำหรับที่ทำการในตัวจังหวัด เช่น 20000 ชลบุรี 37000 อำนาจเจริญ
สองหลักแรกของรหัสไปรษณีย์ไม่ใช่รหัสจังหวัดของกรมการปกครอง สมุทรปราการมีรหัสจังหวัด 11 แต่รหัสไปรษณีย์ขึ้นต้นด้วย 10 เช่น 10270 นนทบุรีรหัสจังหวัด 12 แต่รหัสไปรษณีย์เป็น 11000 สองระบบนี้นับกันคนละชุดตั้งแต่ต้น
ที่สำคัญกว่าตัวเลขคือ รหัสไปรษณีย์ผูกกับพื้นที่ที่บุรุษไปรษณีย์ของที่ทำการนั้นออกไปส่งของ ไม่ได้ผูกกับเส้นเขตการปกครอง ผลที่ตามมามีสองแบบ
- ตำบลเดียวมีได้หลายรหัส ถ้าตำบลนั้นมีที่ทำการหลายแห่งแบ่งกันส่ง อย่าง ต.อ่าวนาง จ.กระบี่ ใช้ 81000 แต่หมู่ 1–6 ใช้ 81180 และหมู่ 7–8 ใช้ 81210 ในกรุงเทพฯ บางอาคารก็มีรหัสของตัวเอง เขตปทุมวันใช้ 10330 แต่อาคารลุมพินีทาวเวอร์ใช้ 10120 และศาลแขวงปทุมวันใช้ 10500
- รหัสเดียวข้ามอำเภอหรือข้ามจังหวัดได้ เมื่อที่ทำการฝั่งไหนเดินทางไปถึงง่ายกว่า อย่าง 80240 ใช้ทั้งที่ อ.เขาพนม จ.กระบี่ และ อ.ทุ่งใหญ่ จ.นครศรีธรรมราช หรือ ต.แม่แดด อ.กัลยาณิวัฒนา ซึ่งอยู่ในเชียงใหม่ แต่ใช้รหัส 58130 ของที่ทำการไปรษณีย์ปาย จ.แม่ฮ่องสอน
ในข้อมูลของไปรษณีย์ไทยที่เราถอดมานับดู มีรหัสไปรษณีย์ราว 190 รหัสที่ใช้มากกว่าหนึ่งอำเภอ และ 14 รหัสที่ข้ามจังหวัด
รหัสไปรษณีย์เปลี่ยนบ่อยกว่าเขตการปกครอง
เขตการปกครองนิ่งกว่าที่คิด อำเภอล่าสุดคือ อ.กัลยาณิวัฒนา ซึ่งตั้งเมื่อปี 2552 ตำบลจะเปลี่ยนเป็นช่วง ๆ เช่น ปี 2560 กรุงเทพมหานครแบ่งแขวงใหม่ใน 7 เขต ยุบแขวงบางนาเป็นบางนาเหนือกับบางนาใต้ ยุบแขวงบางบอนเป็น 4 แขวง และตั้งแขวงใหม่อย่างรัชดาภิเษกกับพญาไท รวม 13 แขวง ต่างจังหวัดก็มีตำบลเปลี่ยนชื่อบ้าง อย่าง ต.หนองเข็ง จ.บึงกาฬ ที่เปลี่ยนเป็น ต.โนนสว่าง
รหัสไปรษณีย์เปลี่ยนบ่อยกว่า เพราะทุกครั้งที่ไปรษณีย์ไทยเปิดที่ทำการใหม่ ก็จะย้ายบางตำบลไปใช้รหัสของที่ทำการนั้น เช่น
- อ.บางเสาธง จ.สมุทรปราการ จาก 10540 เป็น 10570
- อ.วังเจ้า จ.ตาก ไปใช้ 63180 บางรายการยังเป็น 63000 และขนส่งรับทั้งสองรหัส
- 4 ตำบลใน อ.ปากช่อง จาก 30130 เป็น 30450 ที่เล่าไว้ข้างบน
- อ.เฝ้าไร่ จ.หนองคาย ที่เคยใช้ 43120 ของที่ทำการโพนพิสัย ตอนนี้มีที่ทำการเฝ้าไร่ 43230 ของตัวเองแล้ว
ปัญหาจริงจึงไม่ได้อยู่ที่เปลี่ยนบ่อย แต่อยู่ที่แต่ละรายการตามไม่ทัน อย่างปากช่อง ข้อมูลของไปรษณีย์ไทยเองให้ 4 ตำบลนั้นเป็น 30450 มาตั้งแต่ปี 2561 แต่ชุดข้อมูลบน GitHub ทุกชุดที่เราเปิดดูยังเป็น 30130 อยู่ แขวงที่กรุงเทพฯ ประกาศตั้งปี 2560 เพิ่งเข้าไปอยู่ในชุดข้อมูลบน GitHub ที่ใช้กันมากในปี 2567 ถึง 2569 ช้าไป 7–8 ปี ส่วนรายการของขนส่งก็มักเป็นข้อมูลที่ดึงมาครั้งเดียวแล้วใช้ต่อ ในปี 2562 รายการตำบลบนเว็บของขนส่งรายใหญ่เจ้าหนึ่งยังให้บางเสาธงเป็น 10540 อยู่ ทั้งที่ไปรษณีย์ไทยเปลี่ยนไปแล้ว
ชุดข้อมูลที่โหลดมาใช้ก็ไม่ตรงกันเอง
ไปรษณีย์ไทยเผยแพร่รหัสไปรษณีย์ทั่วประเทศ เป็นโปสเตอร์ PDF ที่บอกว่าเป็นข้อมูลสถานะเดือนพฤษภาคม 2561 นอกจากนั้นก็มีหน้าค้นหาบนเว็บ แต่ไม่มีไฟล์ "ตำบล → รหัสไปรษณีย์" ที่เอาไปใช้ในระบบได้ตรง ๆ ที่ผ่านมาเราจึงมักโหลดชุดข้อมูลที่คนในชุมชนรวบรวมไว้บน GitHub มาใช้ ซึ่งช่วยคนทำระบบไทยมาเยอะ แต่ส่วนใหญ่ไม่ได้บอกว่าข้อมูลมาจากไหน
เราลองเอาสองชุดที่ใช้กันมาก คือ kongvut/thai-province-data กับ thailand-geography-json มาเทียบกัน ทั้งสองชุดมีรหัสตำบลตรงกันครบ 7,436 ตำบล แต่ให้รหัสไปรษณีย์ต่างกัน 43 ตำบล
| ตำบล / อำเภอ | ชุดหนึ่ง | อีกชุด | รายการของขนส่ง | ไปรษณีย์ไทย (2561) |
|---|---|---|---|---|
| ต.แม่แดด อ.กัลยาณิวัฒนา | 50250 | 58130 | 58130 | 58130 |
| ทั้ง อ.เฝ้าไร่ จ.หนองคาย | 43230 | 43120 | 43120 | 43120 ตอนนี้มีที่ทำการ 43230 แล้ว |
| อ.ลำทับ จ.กระบี่ (4 ตำบล) | 81190 ทั้งอำเภอ | 81120 ทั้งอำเภอ | ต.ลำทับ 81190 อีก 3 ตำบล 81120 | 81190 ทั้งอำเภอ |
| ต.เกาะเต่า อ.เกาะพะงัน | 84360 | 84280 | 84360 | 84360 |
ช่อง "รายการของขนส่ง" มาจากรายการที่อยู่ที่ GoShip รับสำหรับ Flash Express ที่เราใช้ในงานนั้น จะเห็นว่าไม่มีชุดไหนตรงกับไปรษณีย์ไทยทุกแถว รายการของขนส่งก็เช่นกัน และยังมีแขวงบางนากับแขวงบางบอนที่กรมการปกครองยกเลิกรหัสไปตั้งแต่เมษายน 2565 กับยังใช้ชื่อ ต.หนองเข็ง อยู่ ชื่อสะกดก็ต่างกันได้ เช่น จักรวรรดิ กับ จักรวรรดิ์ หรือ ทุ่งปี้ กับ ทุ่งปี๊
เก็บที่อยู่ในระบบให้รับมือได้
ต่อให้หาชุดข้อมูลที่ดีที่สุดมาได้ ก็จะมีวันที่ไม่ตรงกับขนส่งอยู่ดี ระบบจึงควรออกแบบให้รับเรื่องนี้ได้ตั้งแต่แรก จากงานนั้นเราสรุปไว้แบบนี้
- เก็บรหัสตำบล 6 หลัก ไม่ใช่แค่ชื่อ ชื่อซ้ำกันข้ามจังหวัด และสะกดต่างกันได้ ส่วนรหัสไม่เปลี่ยนตามการสะกด เก็บชื่อที่ลูกค้าเห็นตอนกรอกไว้ด้วย เผื่อชื่อเปลี่ยนภายหลัง
- ให้ตำบลหนึ่งมีได้หลายรหัสไปรษณีย์ ถ้ามีรหัสเดียวก็เติมให้เลย ถ้ามีหลายรหัสให้ลูกค้าเลือก
- เก็บรหัสไปรษณีย์เป็นข้อความ ไม่ใช่ตัวเลข จะได้ไม่มีปัญหาเรื่องรูปแบบตอนส่งต่อให้ระบบอื่น
- เตือน แต่อย่าบล็อก ถ้าลูกค้ากรอกรหัสที่ไม่มีในรายการของเรา ให้บันทึกได้ แล้วติดธงให้ทีมเช็ก ลูกค้ารู้จักรหัสบ้านตัวเองดีกว่าชุดข้อมูลของเรา
- จำชื่อเก่าไว้ ชื่อตำบลที่ยุบหรือเปลี่ยนชื่อแล้วยังโผล่มาจากที่อยู่เก่าของลูกค้าและจากรายการของขนส่ง เก็บไว้เป็นชื่อเรียกอื่นที่ชี้ไปตำบลใหม่
- บันทึกว่าใช้ชุดข้อมูลรุ่นไหน มาจากไหน พอแหล่งข้อมูลเปลี่ยน จะได้รู้ว่าต้องเช็กอะไร
- เทียบกับรายการของขนส่งก่อนใช้งานจริง ขนส่งส่วนใหญ่มีรายการที่อยู่ที่รับ หรือมี API ตรวจที่อยู่ ไล่หาแถวที่ไม่ตรงแล้วเติมให้ครบ
ใน Laravel ตารางรหัสไปรษณีย์หน้าตาประมาณนี้
Schema::create('subdistrict_postcodes', function (Blueprint $table) {
$table->char('subdistrict_code', 6);
$table->char('postcode', 5);
$table->string('source');
$table->string('note')->nullable();
$table->primary(['subdistrict_code', 'postcode']);
});
Schema::create('addresses', function (Blueprint $table) {
$table->id();
$table->string('line');
$table->char('subdistrict_code', 6)->index();
$table->string('subdistrict');
$table->string('district');
$table->string('province');
$table->char('postcode', 5);
$table->boolean('needs_review')->default(false);
$table->timestamps();
});
คีย์หลักของ subdistrict_postcodes เป็นคู่ของรหัสตำบลกับรหัสไปรษณีย์ ตำบลหนึ่งจึงมีได้หลายแถว ส่วน source บอกว่ารหัสนั้นมาจากไหน ส่วนตาราง addresses เก็บทั้งรหัสตำบลและชื่อที่ลูกค้าเห็น และมี needs_review ไว้ติดธงเวลารหัสไม่ตรงกับรายการ
ชุดข้อมูลที่บอกที่มาทุกแถว
หลังจากงานนั้น เรารวบรวมชุดข้อมูลที่อยู่ของเราเองไว้ที่ข้อมูลจังหวัด อำเภอ ตำบล และรหัสไปรษณีย์ ใช้รหัสและชื่อจากกรมการปกครอง ใช้รหัสไปรษณีย์จากไปรษณีย์ไทย และทุกแถวที่แก้จากต้นฉบับมีหลักฐานกำกับ แต่ละรุ่นบอกวันที่ของทุกแหล่ง ค้นดูในหน้านั้นได้เลย หรือดาวน์โหลดไปใช้ได้ทั้ง JSON, CSV, SQL และ Excel
เราไม่ได้บอกว่าชุดนี้ถูก 100% แต่ทุกแถวตรวจย้อนได้ว่ามาจากไหน ถ้าเจอแถวที่ไม่ตรง บอกเราได้เลยครับ






