NULL ใน SQL: กับดักที่คาดไม่ถึงในเงื่อนไข WHERE
sqlpostgresqldatabasenullwhereclausequery
14 ก.ค. 68 , 14:29
เคยสงสัยไหมว่าทำไมคิวรี่ != ถึงไม่ดึงข้อมูลแถวที่เป็น NULL ออกมา? บทความนี้จะอธิบายถึงพฤติกรรมของ NULL ใน SQL และวิธีเขียนเงื่อนไขเพื่อจัดการกับ "ค่าที่ไม่ทราบค่า" นี้อย่างถูกต้อง

หนึ่งในกับดักที่นักพัฒนา SQL ทั้งมือใหม่และมือเก๋าอาจเคยเจอ คือการที่ผลลัพธ์จากการคิวรี่ไม่เป็นไปตามที่คาดหวัง โดยเฉพาะเมื่อต้องจัดการกับคอลัมน์ที่มีค่า NULL

ลองจินตนาการถึงสถานการณ์นี้

เรามีตารางชื่อ orders ที่เก็บข้อมูลคำสั่งซื้อสินค้า และมีคอลัมน์ shipped_date เพื่อบอกวันที่สินค้าถูกจัดส่ง ถ้า shipped_date เป็น NULL แสดงว่าสินค้านั้นยังไม่ถูกจัดส่ง

ตาราง orders

Column 1Column 2

สมมติว่าเราต้องการหารายการสั่งซื้อทั้งหมดที่ ไม่ได้ถูกจัดส่งในวันที่ 2025-07-11 โดยเราอาจจะเขียนคิวรี่ง่ายๆ แบบนี้:

-- คิวรี่ที่ดูเหมือนจะถูกต้อง
SELECT order_id, product_name, shipped_date
FROM orders
WHERE shipped_date != '2025-07-11';

เราคาดหวังว่าจะได้ผลลัพธ์ 3 แถว คือ order_id 101 (ส่งวันอื่น), 103 (ยังไม่ส่ง) และ 105 (ยังไม่ส่ง) แต่ผลลัพธ์ที่ได้กลับมามีเพียงแถวเดียว:

Column 1Column 2

เกิดอะไรขึ้น? ทำไมรายการที่ยังไม่ถูกส่ง (shipped_date เป็น NULL) ถึงหายไป?

หัวใจของปัญหานี้คือความเข้าใจผิดเกี่ยวกับ NULL ในโลกของฐานข้อมูล NULL ไม่ใช่ค่าที่เท่ากับ 0, ไม่ใช่ช่องว่าง, และไม่ใช่ "ไม่มีอะไร" แต่มันคือ สถานะ (state) ที่บ่งบอกว่า "ไม่ทราบค่า" (unknown) หรือ "ไม่มีข้อมูล" (missing value)

ด้วยเหตุนี้ ระบบจัดการฐานข้อมูลจึงใช้ตรรกะแบบสามค่า (Three-Valued Logic หรือ 3VL) ซึ่งประกอบด้วย:

  1. TRUE (จริง)
  1. FALSE (เท็จ)
  1. UNKNOWN (ไม่ทราบ)

เมื่อคุณนำ NULL ไปเปรียบเทียบกับค่าใดๆ ด้วยโอเปอเรเตอร์ทางตรรกะ (=, !=, <, >) ผลลัพธ์ที่ได้จะเป็น UNKNOWN เสมอ

  • NULL = '2025-07-11' → UNKNOWN
  • NULL != '2025-07-11' → UNKNOWN
  • NULL = NULL → UNKNOWN

เงื่อนไข WHERE ใน SQL จะดึงข้อมูลเฉพาะแถวที่ผลลัพธ์ของเงื่อนไขเป็น TRUE เท่านั้น มันจะทิ้งแถวที่ผลลัพธ์เป็น FALSE และ UNKNOWN ไปทั้งหมด นี่คือสาเหตุที่แถว order_id 103 และ 105 หายไปจากผลลัพธ์ของเรา

วิธีแก้ไข: จัดการ NULL อย่างชัดเจน

เพื่อให้ได้ผลลัพธ์ตามที่ต้องการ เราต้องเขียนเงื่อนไขเพื่อจัดการกับ NULL โดยเฉพาะ เป็นวิธีที่ตรงไปตรงมาที่สุด คือการเพิ่มเงื่อนไขเข้าไปว่า "หรือค่าในคอลัมน์นั้นต้องเป็น NULL"

SELECT order_id, product_name, shipped_date
FROM orders
WHERE (shipped_date != '2025-07-11' OR shipped_date IS NULL);

ผลลัพธ์ที่ถูกต้อง:

Column 1Column 2

ตอนนี้คิวรี่ทำงานถูกต้องแล้ว เพราะมันเลือกแถวที่ shipped_date ไม่ใช่วันที่ '2025-07-11' หรือ แถวที่ shipped_date เป็น NULL

วิธีที่ 2: ใช้ฟังก์ชัน COALESCE

COALESCE เป็นฟังก์ชันที่มีประโยชน์มากในการจัดการ NULL มันจะคืนค่าแรกที่ไม่ใช่ NULL จากรายการที่เราส่งเข้าไป เราสามารถใช้มันเพื่อแปลง NULL ให้เป็นค่าอื่นชั่วคราวเพื่อการเปรียบเทียบได้

-- กำหนดให้ NULL มีค่าเทียบเท่ากับวันที่ในอดีตไปเลย
SELECT order_id, product_name, shipped_date
FROM orders
WHERE COALESCE(shipped_date, '1900-01-01'::date) != '2025-07-11'::date;

ในคิวรี่นี้ ถ้า shipped_date เป็น NULL มันจะถูกแทนที่ด้วยวันที่ '1900-01-01' ก่อนนำไปเปรียบเทียบ ทำให้เงื่อนไข '1900-01-01' != '2025-07-11' เป็น TRUE และได้ผลลัพธ์ที่ถูกต้องเช่นกัน

การเปรียบเทียบกับ NULL เป็นหนึ่งในข้อผิดพลาดที่พบบ่อยที่สุดในการเขียน SQL จงจำไว้เสมอว่า NULL หมายถึง "ไม่ทราบค่า" และการเปรียบเทียบใดๆ กับมันจะให้ผลลัพธ์เป็น UNKNOWN เสมอ ดังนั้น หากต้องการจัดการกับแถวที่มีค่า NULL ในเงื่อนไข WHERE คุณต้องระบุอย่างชัดเจนโดยใช้ IS NULL หรือ IS NOT NULL เสมอครับ