ทำไมต้องรวมข้อมูลจากหลายตารางด้วย Joins
เวลาเราทำแอปพลิเคชัน ข้อมูลมักไม่ได้ถูกเก็บไว้ในตารางเดียวทั้งหมด เพราะถ้าเก็บรวมกันหมดจะทำให้ข้อมูลซ้ำซ้อนและจัดการยาก Database Normalization (กระบวนการจัดระเบียบข้อมูลเพื่อลดความซ้ำซ้อน) จึงแยกข้อมูลออกเป็นตารางย่อยๆ ตามความเกี่ยวข้อง
สมมติว่าเรามีตาราง Customers (ข้อมูลลูกค้า) และตาราง Orders (รายการสั่งซื้อ) ถ้าเราอยากรู้ว่าลูกค้าคนไหนสั่งซื้ออะไรบ้าง เราต้องเอาสองตารางนี้มาเชื่อมกันผ่านตัวเชื่อมที่เรียกว่า Joins (คำสั่งเชื่อมข้อมูลจากหลายตารางเข้าด้วยกัน)
การเข้าใจเรื่อง Joins เป็นหัวใจสำคัญของงานสาย Backend (การเขียนโปรแกรมฝั่งเซิร์ฟเวอร์) เพราะในการทำงานจริง คุณจะต้องดึงข้อมูลที่กระจัดกระจายมารวมกันเป็นรายงานหรือหน้าจอให้ผู้ใช้งานดูได้ถูกต้องเสมอ ถ้าทำไม่เป็น คุณจะเสียเวลาเขียนโค้ดดึงข้อมูลแยกกันหลายรอบซึ่งทำให้ระบบทำงานช้า
INNER JOIN: เชื่อมเฉพาะข้อมูลที่มีคู่กัน
INNER JOIN คือการดึงข้อมูลที่ Match (มีค่าตรงกัน) ทั้งสองตารางมาแสดงเท่านั้น ถ้าฝั่งใดฝั่งหนึ่งไม่มีข้อมูลที่สัมพันธ์กัน ข้อมูลแถวนั้นจะถูกตัดออกไปทันที เปรียบเหมือนการนัดเพื่อนไปกินข้าว เราจะเจอหน้ากันเฉพาะคนที่มีชื่ออยู่ในรายชื่อนัดและมาปรากฏตัวที่ร้านจริง
เรามักใช้คำสั่งนี้เมื่อต้องการข้อมูลที่สมบูรณ์เท่านั้น เช่น ดึงรายชื่อลูกค้าที่เคยมีการสั่งซื้อสินค้าออกมาดู ถ้าลูกค้าคนไหนยังไม่เคยสั่งซื้อเลย เขาจะไม่ปรากฏในรายงานนี้ ซึ่งเป็นเรื่องปกติเพราะเราสนใจแค่คนที่ทำรายการแล้ว
จุดที่มือใหม่พลาดบ่อยคือการลืมกำหนด ON (เงื่อนไขในการเชื่อมตาราง) ซึ่งจะทำให้เกิดผลลัพธ์แบบผิดพลาดที่เรียกว่า Cartesian Product (การนำทุกแถวจากตารางแรกไปจับคู่กับทุกแถวของตารางที่สอง) จนข้อมูลบวมมหาศาลและค้างได้
-- ดึงชื่อลูกค้าและเลขที่ใบสั่งซื้อ
SELECT Customers.Name, Orders.OrderID
FROM Customers
INNER JOIN Orders ON Customers.CustomerID = Orders.CustomerID;
คำอธิบายโค้ด: SELECT คือการเลือกว่าจะเอาคอลัมน์ไหนมาดูบ้าง INNER JOIN เป็นตัวสั่งให้เชื่อมตาราง Orders เข้ากับ Customers โดยใช้ ON เป็นตัวกำหนดว่าคอลัมน์ CustomerID ของทั้งสองตารางต้องมีค่าเท่ากันถึงจะดึงออกมาแสดง
ผลลัพธ์ที่ควรเห็น: จะแสดงรายการชื่อลูกค้าคู่กับเลขที่ใบสั่งซื้อเฉพาะลูกค้าที่มีประวัติการสั่งซื้อเท่านั้น หากลูกค้าคนไหนไม่เคยสั่งซื้อ ชื่อเขาจะไม่ถูกดึงออกมาแสดงในรายการนี้เลย
LEFT JOIN: เก็บข้อมูลฝั่งซ้ายไว้ครบ
LEFT JOIN คือการดึงข้อมูลจากตารางฝั่งซ้าย (ตารางหลัก) มาทั้งหมด ต่อให้ฝั่งขวาไม่มีข้อมูลที่ตรงกันก็ตาม ถ้าฝั่งขวาไม่มีคู่ ข้อมูลในคอลัมน์ฝั่งขวาจะแสดงเป็นค่าว่างหรือ NULL (ค่าที่ไม่มีข้อมูล) แทน
คำสั่งนี้มีประโยชน์มากตอนที่เราต้องการดูภาพรวม เช่น อยากลิสต์รายชื่อลูกค้าทุกคนออกมา แม้ว่าบางคนจะยังไม่เคยสั่งซื้อเลยก็ตาม เราจะใช้ LEFT JOIN เพื่อเช็กว่าใครบ้างที่ยังเป็นลูกค้าใหม่ที่ยังไม่ยอมกดสั่งซื้อสินค้า
การเลือกใช้ LEFT JOIN ช่วยให้เราเห็นข้อมูลครบถ้วนตามตารางหลักที่เราสนใจ ทำให้เราสามารถวิเคราะห์พฤติกรรมลูกค้าได้ละเอียดขึ้น ต่างจากการใช้ INNER JOIN ที่จะทำข้อมูลหายไปบางส่วนโดยที่เราไม่รู้ตัว
-- ดึงรายชื่อลูกค้าทุกคน แม้ยังไม่เคยสั่งซื้อ
SELECT Customers.Name, Orders.OrderID
FROM Customers
LEFT JOIN Orders ON Customers.CustomerID = Orders.CustomerID;
คำอธิบายโค้ด: LEFT JOIN จะยึดตาราง Customers เป็นหลัก ไม่ว่าจะมีข้อมูลใน Orders หรือไม่ก็ตาม ถ้าไม่มีข้อมูลใน Orders ระบบจะใส่ค่า NULL ให้ในช่องของ OrderID โดยอัตโนมัติ
ผลลัพธ์ที่ควรเห็น: รายชื่อลูกค้าทุกคนจะปรากฏขึ้นมา หากลูกค้าคนไหนไม่เคยสั่งซื้อ ช่อง OrderID จะว่างเปล่า แต่ชื่อลูกค้าจะยังคงอยู่ครบทุกคน
RIGHT JOIN และ FULL OUTER JOIN: ทางเลือกที่เหลือ
RIGHT JOIN ทำงานตรงข้ามกับ LEFT JOIN ทุกประการ โดยจะยึดตารางฝั่งขวาเป็นหลักและดึงข้อมูลฝั่งซ้ายมาเติม ถ้าฝั่งซ้ายไม่มีคู่ก็จะเป็น NULL ส่วนใหญ่โปรแกรมเมอร์มักเลี่ยงไปใช้ LEFT JOIN แทนเพื่อให้โค้ดอ่านง่ายและเป็นมาตรฐานเดียวกัน
FULL OUTER JOIN คือการรวมร่างข้อมูลทั้งหมดจากทั้งสองตาราง ไม่ว่าจะมีคู่หรือไม่ก็ตาม ถ้าไม่มีคู่ก็จะเติม NULL ให้ทั้งสองฝั่ง เหมาะสำหรับงานวิเคราะห์ข้อมูลที่ต้องการเห็นภาพรวมทั้งหมดของทั้งสองระบบว่ามีอะไรหลุดรอดไปบ้าง
จำไว้ว่าการใช้ FULL OUTER JOIN อาจจะทำให้ฐานข้อมูลทำงานหนักหากตารางมีขนาดใหญ่มาก เพราะระบบต้องไล่ตรวจเช็กข้อมูลทั้งหมดทั้งสองฝั่ง ควรใช้เฉพาะกรณีที่จำเป็นต้องทำรายงานสรุปข้อมูลแบบละเอียดจริงๆ เท่านั้น
-- ดึงข้อมูลทั้งหมดจากทั้งสองตาราง
SELECT Customers.Name, Orders.OrderID
FROM Customers
FULL OUTER JOIN Orders ON Customers.CustomerID = Orders.CustomerID;
คำอธิบายโค้ด: FULL OUTER JOIN จะรวมข้อมูลทุกอย่าง ถ้าลูกค้าไม่มีการสั่งซื้อจะเห็นชื่อลูกค้าแต่เลขคำสั่งซื้อเป็น NULL และถ้ามีรายการสั่งซื้อที่ไม่มีเจ้าของ (กรณีข้อมูลผิดปกติ) ก็จะเห็นเลขคำสั่งซื้อแต่ชื่อลูกค้าเป็น NULL
ผลลัพธ์ที่ควรเห็น: รายการที่รวมเอาทุกความเป็นไปได้มาแสดง ทั้งลูกค้าที่สั่งซื้อแล้ว ลูกค้าที่ยังไม่สั่งซื้อ และรายการสั่งซื้อที่อาจจะตกหล่นไม่ตรงกับลูกค้าในระบบ
CROSS JOIN: การจับคู่แบบทวีคูณ
CROSS JOIN คือการนำทุกแถวจากตารางแรกไปจับคู่กับทุกแถวในตารางที่สอง ผลลัพธ์ที่ได้จะมีจำนวนมหาศาล เพราะมันไม่มีเงื่อนไข ON มาคอยคัดกรองข้อมูลให้เราเลยแม้แต่นิดเดียว
คุณอาจจะสงสัยว่าเราจะใช้มันทำไม? ส่วนใหญ่ใช้ในการทำ Test Data (ข้อมูลจำลองสำหรับการทดสอบ) หรือการสร้างตารางเมทริกซ์ เช่น การจับคู่สินค้าทุกตัวกับไซส์เสื้อผ้าที่มีทั้งหมด เพื่อดูว่าเรามีสินค้าครบทุกสีทุกไซส์หรือยัง
ต้องระวังให้มากเมื่อใช้คำสั่งนี้กับตารางที่มีข้อมูลหลักหมื่นหลักแสนแถว เพราะถ้าตารางซ้ายมี 1,000 แถว และตารางขวามี 1,000 แถว ผลลัพธ์จะออกมาถึง 1,000,000 แถว ซึ่งอาจทำให้โปรแกรมของคุณแฮงค์หรือฐานข้อมูลล่มได้เลย
-- สร้างชุดข้อมูลจับคู่สินค้ากับสี
SELECT Products.Name, Colors.ColorName
FROM Products
CROSS JOIN Colors;
คำอธิบายโค้ด: คำสั่งนี้ไม่มี ON เพราะมันต้องการให้สินค้าทุกตัวไปจับคู่กับทุกสีที่มีอยู่ในตาราง Colors แบบเหมาเข่งทั้งหมดโดยไม่สนเงื่อนไขอื่น
ผลลัพธ์ที่ควรเห็น: รายการที่แสดงสินค้าทุกชิ้นซ้ำตามจำนวนสีทั้งหมดที่มี เช่น ถ้ามีสินค้า 3 อย่าง และสี 3 สี จะได้ผลลัพธ์ออกมาเป็น 9 แถว
สรุปการนำไปใช้จริงในงานโปรแกรมเมอร์
การเลือกใช้ Joins ให้ถูกต้องจะช่วยให้โค้ดของคุณมีประสิทธิภาพและอ่านง่าย หากคุณทำงานเป็น Junior Developer (นักพัฒนาซอฟต์แวร์ระดับต้น) ให้เริ่มฝึกจาก INNER JOIN และ LEFT JOIN ให้คล่องก่อน เพราะใช้บ่อยที่สุดในการเขียนเว็บแอปพลิเคชัน
ในสถานการณ์จริง เช่น การทำฟีเจอร์ "ตะกร้าสินค้า" คุณต้องใช้ INNER JOIN เพื่อดึงรายการสินค้าที่ลูกค้าเลือกออกมาแสดงคู่กับข้อมูลราคาจากตารางสินค้า หากคุณใช้ผิดประเภท ข้อมูลราคาอาจจะโชว์ไม่ครบหรือแสดงผลผิดพลาด ทำให้ระบบคำนวณเงินผิดได้
จงฝึกเขียนคำสั่ง Query บ่อยๆ ด้วยข้อมูลจำลอง แล้วลองสังเกตผลลัพธ์ที่ได้ในแต่ละแบบ การทดลองผิดทดลองถูกด้วยตัวเองจะทำให้คุณจำแม่นกว่าการอ่านทฤษฎีเพียงอย่างเดียว เมื่อคุณเข้าใจพื้นฐานเหล่านี้แล้ว การขยับไปทำระบบที่ซับซ้อนขึ้นก็จะไม่ใช่เรื่องยากอีกต่อไป