หนึ่งในกับดักที่นักพัฒนา SQL ทั้งมือใหม่และมือเก๋าอาจเคยเจอ คือการที่ผลลัพธ์จากการคิวรี่ไม่เป็นไปตามที่คาดหวัง โดยเฉพาะเมื่อต้องจัดการกับคอลัมน์ที่มีค่า NULL
ลองจินตนาการถึงสถานการณ์นี้
เรามีตารางชื่อ orders ที่เก็บข้อมูลคำสั่งซื้อสินค้า และมีคอลัมน์ shipped_date เพื่อบอกวันที่สินค้าถูกจัดส่ง ถ้า shipped_date เป็น NULL แสดงว่าสินค้านั้นยังไม่ถูกจัดส่ง
ตาราง orders
| order_id | product_name | customer_id | shipped_date | 
|---|---|---|---|
| 101 | Keyboard | 12 | 2025-07-10 | 
| 102 | Mouse | 15 | 2025-07-11 | 
| 103 | Monitor | 12 | NULL | 
| 104 | Webcam | 21 | 2025-07-11 | 
| 105 | USB Hub | 15 | NULL | 
สมมติว่าเราต้องการหารายการสั่งซื้อทั้งหมดที่ ไม่ได้ถูกจัดส่งในวันที่ 2025-07-11 โดยเราอาจจะเขียนคิวรี่ง่ายๆ แบบนี้:
เราคาดหวังว่าจะได้ผลลัพธ์ 3 แถว คือ order_id 101 (ส่งวันอื่น), 103 (ยังไม่ส่ง) และ 105 (ยังไม่ส่ง) แต่ผลลัพธ์ที่ได้กลับมามีเพียงแถวเดียว:
| order_id | product_name | shipped_date | 
|---|---|---|
| 101 | Keyboard | 2025-07-10 | 
เกิดอะไรขึ้น? ทำไมรายการที่ยังไม่ถูกส่ง (shipped_date เป็น NULL) ถึงหายไป?
หัวใจของปัญหานี้คือความเข้าใจผิดเกี่ยวกับ NULL ในโลกของฐานข้อมูล NULL ไม่ใช่ค่าที่เท่ากับ 0, ไม่ใช่ช่องว่าง, และไม่ใช่ "ไม่มีอะไร" แต่มันคือ สถานะ (state) ที่บ่งบอกว่า "ไม่ทราบค่า" (unknown) หรือ "ไม่มีข้อมูล" (missing value)
ด้วยเหตุนี้ ระบบจัดการฐานข้อมูลจึงใช้ตรรกะแบบสามค่า (Three-Valued Logic หรือ 3VL) ซึ่งประกอบด้วย:
เมื่อคุณนำ NULL ไปเปรียบเทียบกับค่าใดๆ ด้วยโอเปอเรเตอร์ทางตรรกะ (=, !=, <, >) ผลลัพธ์ที่ได้จะเป็น UNKNOWN เสมอ
เงื่อนไข WHERE ใน SQL จะดึงข้อมูลเฉพาะแถวที่ผลลัพธ์ของเงื่อนไขเป็น TRUE เท่านั้น มันจะทิ้งแถวที่ผลลัพธ์เป็น FALSE และ UNKNOWN ไปทั้งหมด นี่คือสาเหตุที่แถว order_id 103 และ 105 หายไปจากผลลัพธ์ของเรา
วิธีแก้ไข: จัดการ NULL อย่างชัดเจน
เพื่อให้ได้ผลลัพธ์ตามที่ต้องการ เราต้องเขียนเงื่อนไขเพื่อจัดการกับ NULL โดยเฉพาะ เป็นวิธีที่ตรงไปตรงมาที่สุด คือการเพิ่มเงื่อนไขเข้าไปว่า "หรือค่าในคอลัมน์นั้นต้องเป็น NULL"
ผลลัพธ์ที่ถูกต้อง:
| order_id | product_name | shipped_date | 
|---|---|---|
| 101 | Keyboard | 2025-07-10 | 
| 103 | Monitor | NULL | 
| 105 | USB Hub | NULL | 
ตอนนี้คิวรี่ทำงานถูกต้องแล้ว เพราะมันเลือกแถวที่ shipped_date ไม่ใช่วันที่ '2025-07-11' หรือ แถวที่ shipped_date เป็น NULL
วิธีที่ 2: ใช้ฟังก์ชัน COALESCE
COALESCE เป็นฟังก์ชันที่มีประโยชน์มากในการจัดการ NULL มันจะคืนค่าแรกที่ไม่ใช่ NULL จากรายการที่เราส่งเข้าไป เราสามารถใช้มันเพื่อแปลง NULL ให้เป็นค่าอื่นชั่วคราวเพื่อการเปรียบเทียบได้
ในคิวรี่นี้ ถ้า shipped_date เป็น NULL มันจะถูกแทนที่ด้วยวันที่ '1900-01-01' ก่อนนำไปเปรียบเทียบ ทำให้เงื่อนไข '1900-01-01' != '2025-07-11' เป็น TRUE และได้ผลลัพธ์ที่ถูกต้องเช่นกัน