แปล? กฏ 10 ข้อเพื่อโครงสร้าง SQL ที่ดี

จะเรียกว่าแปลแบบเต็มตัวก็ไม่ได้เพราะอยากโน้ตไว้อ่านเองมากกว่า กับ 10 กฏที่ช่วยให้โครงสร้างฐานข้อมูลแบบ Relational database ดีขึ้น

1. Lowercase, number, and underscore เท่านั้น

ด้วยความที่ว่าเวลาเราเรียกใช้โครงสร้างมักจะอยู่ในรูป database.schema.table.column ดังนั้นห้ามมี dot, space, dash หรือ uppercase เลยจะดีที่สุด ไม่เชื่อลองพิมพ์ statement ด้านล่างนี้ดูสิว่าคุณชอบแบบไหนมากกว่ากัน

SELECT "User name" FROM events
-- versus
SELECT user_name FROM events

2. ตั้งชื่อคอลัมน์ง่ายๆ แต่ให้สื่อความหมาย

ข้อนี้ผมเจอบ่อยมาก หลายคนชอบตั้งให้สั้นเพื่อความสะดวกของตัวเอง แล้วถ้าคนอื่นมาดูจะรู้เรื่องไหม? เช่น foreign key ชื่อ package_id ดันไปย่อเป็น pkg_fk แล้วใครมันจะไปตรัสรู้ได้ล่ะ

ชื่อที่ไม่สื่อเลยอย่าง item_value หรือ item_type ต่อให้คุณเข้าใจเองก็เถอะ คนที่เอาไปใช้ต่อก็ต้องเสียเวลาอยู่ดี

SELECT SUM(photo_count) FROM items
-- versus
SELECT SUM(item_value) AS photo_count

อีกประเภทคือที่ชอบเอาชื่อ table มาเป็น prefix ซ้ำซ้อนอย่าง สมมุตินะถ้านี้คือตาราง users แล้วมี user_first_name, user_birthday ทำไมคนที่จะนำไปใช้ต้องต้องเสียเวลาพิมพ์ user_ เพิ่มด้วยว่ะครับ

3. ชื่อตารางเอาให้สั้นและสื่อ

ถ้าตารางของเรามีมากกว่าหนึ่งคำให้เชื่อมด้วย underscore อย่าปล่อยให้มันเลื่อยยาวไปอย่าง packagedeliveries เขียนเป็น package_deliveries ดูง่ายกว่าไหม และถ้าเป็นไปได้ให้เหลือแค่ deliveries เลยดีกว่า

อย่าเอา prefix ไปใส่หน้าชื่อตาราง อันนี้ก็ฮอตฮิตเจอบ่อยมากอย่างตารางชื่อ tbl_users เนี้ยไม่รู้ว่าจะใส่ tbl_ เพราะใครๆ ก็รู้อยู่แล้วว่ามันคือตารางจะไปย่อว่ามันคือ table users ทำหอกอะไร

ส่วนเรื่องการตั้งชื่อตารางเป็นเอกนามหรือพหูนามอันนี้ผมให้แล้วแต่ทีมเลย แต่ถ้าเลือกใช้พหูนามก็ให้เหมือนกันหมดด้วยนะ เช่น packages_owners

4. ใช้คีย์หลักเป็นจำนวนเต็ม

คือบางตารางอาจจะใช้คีย์หลักอย่างรหัสนักศึกษาจึงอาจใช้เป็น VARCHAR ก็ไม่ว่ากันแต่นั้นหมายความว่าการเพิ่ม record ใหม่ๆ คุณต้องนั่งกรอกเองนะ จะดีกว่าไหมถ้าปล่อยให้เป็น auto increment integer วิ่งไปเรื่อยๆ แทน

และอีกอย่างที่พบได้บ่อยคือใช้หลายคอลัมน์เป็นคีย์หลัก เช่น ข้อมูลวิชาเรียนที่ใช้ semester, year, course_no, section รวมกันเป็นคีย์หลัก มันก็ไม่ได้ผิดอะไรนะ แต่เวลาผมจะทำอะไรกับมันผมต้องใส่เงื่อนไขทั้ง 4 ตัวเลยเหรอ จะง่ายกว่าไหมถ้าเหลือแค่ integer ตัวเดียว

5. ใช้ Foreign key ที่มันเกี่ยวข้องกัน

อันนี้ขอยกข้อดีจากการตั้งชื่อตารางแบบเอกนามนะ สมมุติถ้าตาราง

student

idfirst_namelast_name12345foobar

grade

idstudent_idalphabet112345A212345B+

ตรง grade.student_id นี้สื่อได้ตรงเลยว่ามันคือ foreign key ที่ชื่อไปหาคอลัมน์ id ของตาราง student แน่นอน

แต่มันก็ไม่จำเป็นต้องให้ตรงกันแบบนี้เสมอไปนะตัวอย่างถ้าเราต้องเก็บข้อมูลอาจารย์ที่ปรึกษาซึ่งเป็นผู้สอนด้วยใช้ชื่อคอลัมน์ว่า advisor_id อาจจะสื่อกว่า

instructor

idfirst_namelast_name678johndoe

student

idfirst_namelast_nameadvisor_id12345foobar678

6. อย่าเก็บพวกวันเวลาเป็น String

อันนี้ก็เด็ด ผมเจอบ่อยมากที่คอลัมน์จะใช้เก็บข้อมูเวลาแต่พอเอาเข้าจริงดัน insert ดันใช้การแปลงมาจาก string เฉยเลย

$sql = "INSERT INTO user_log (user_id, updated_at) VALUES (1, " . date() . ")";

มันจะง่ายกว่าไหมถ้าเหลือแค่

$sql = "INSERT INTO user_log (user_id, updated_at) VALUES (1, NOW())";

ที่ยังแค่ insert ยังเหนือยขนาดนี้ แล้วถ้า select, search ล่ะจะขนาดไหนไม่อยากนึกถึงเลย

7. ถ้าจะเก็บตัวแปรชนิด timezone ให้เก็บเป็น UTC

ด้วยความยืดหยุ่นที่เราสามารถเก็บได้หลาย timezone ทำให้มีโอกาสที่ในระบบหนึ่งจะเกิดการ insert ของ timestamp มากกว่า 1 timezone สุดท้ายมันจะทำให้การจัดการยุ่งยากขึ้นมาก ดังนั้นตอนเก็บให้เป็น UTC ดีกว่าแล้วเวลาจะเรียกใช้ค่อย convert เอาด้วยวิธีง่ายๆ เช่น

SELECT id, email, [created_at:pst] FROM users

8. ให้มันมีสักอันเดียวเถอะ

อย่างการออกแบบตารางผู้ใช้ที่ต้องบันทึกได้ว่าผู้ใช้มีรหัสเดิมคืออะไรแล้วทำให้เกิดคอลัมน์ user_id กับ user_id_old เนี้ยเลิกเถอะครับ แล้วตัวอย่างที่ว่ามาถ้าผู้ใช้เกิดเปลี่ยนรหัสรอบที่ 3 ล่ะจะเก็บยังไง?

9. อย่าสร้างตารางที่โตแนวกว้าง

อันนี้ก็เด็ดครับและผมเคยเจอกับตัวด้วย มีโปรแกรมเมอร์คนหนึ่งที่ทำงานjได้โจทย์ให้เขียนโปรแกรมเก็บ mac address ของผู้ใช้

user

idmac_address1XXX

แล้วพอมีเคสว่าผู้ใช้หนึ่งคนอาจจะมีได้หลายเครื่อง โปรแกรมเมอร์ท่านนี้ก็งอกเป็นตารางที่โนแนวกว้างเฉยเลย

idmac_address1mac_address21XXXYYY2CCCNULL

นอกจากจะเก็บไม่ได้เรื่องแล้วยัง search ยากอีก ถ้าพี่จะเขียนแบบนี้ไป NoSQL หรือเป็น JSON ดีกว่านะครับ

10. Normalize มากเกินไป

อันนี้ผมไม่เจอกับตัวนะแต่ลองนึกภาพว่าถ้าเราเก็บข้อมูลที่อยู่ของผู้ใช้แบบนี้ล่ะ

user_address

iduser_idaddress_idcity_idcountry_id11345

address

idaddress3Sudo Apartment, Atom street

งั้นเวลาใช้เราก็ต้องมา join ตารางอีกล่ะสิ เพื่ออะไรเนี้ย

SELECT
*
FROM user_address
JOIN address ON user_address.address_id = address.id

Ref https://www.periscopedata.com/blog/better-sql-schema.html?utm_source=dbweekly&utm_medium=email

Like what you read? Give Titipat a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.