go to http://oracle.in.th

Saturday, April 10, 2010

ค่า Null ใน Oracle

หลายคนที่เคยใช้งานฐานข้อมูลมาระยะหนึ่ง จะต้องรู้จักกับค่าว่าง หรือค่า Null ซึ่งมีความพิเศษไม่เหมือนใครอยู่หลายอย่าง และเป็นหลุมพรางที่ก่อให้เกิด bug ใน application มาแล้วนับไม่ถ้วน วันนี้ ผมจึงอยากชวนให้ผู้อ่านมาทำความรู้จักกับเจ้าค่าว่างนี้ กันให้มากขึ้น ซึ่งน่าจะเป็นประโยชน์ทั้งกับผู้ที่เพิ่งเริ่มต้นหัดเขียน SQL ใหม่ ๆ และนักพัฒนาที่มีประสบการณ์แล้วทุกท่าน

ความหมายและการใช้งาน

ความหมายของค่า Null ซึ่งถูกกำหนดขึ้นโดย E.F.Codd ผู้ให้กำเนิด Relational Model ซึ่งเป็นที่มา RDBMS ทั้งหลาย คือ “missing information and inapplicable information” แปลว่า ข้อมูลที่ไม่ถูกระบุหรือไม่สามารถระบุได้ โดยทั่วไปเราจะใช้ในความหมายว่า ข้อมูลที่ไม่ทราบค่า (unknown value)

ตาม Oracle Database SQL Language Reference Oracle แนะนำให้ระบุค่า Null กรณีต่อไปนี้

  1. when the actual value is not known เราไม่ทราบค่าที่แท้จริงของข้อมูลนั้น หรือ
  2. when a value would not be meaningful ค่าที่ใส่ให้กับข้อมูลนั้นไม่สื่อความหมายที่แท้จริง

กรณีที่ 1 นั้นตรงไปตรงมา ถ้าเราไม่รู้ค่าของข้อมูล เราก็ไม่ต้องใส่อะไรลงไป ฐานข้อมูลก็จะ assign ค่า Null ให้กับข้อมูลนั้น ส่วนกรณีที่ 2 ผมอยากจะเข้าใจว่า Oracle หมายถึงกรณีที่เราใส่ค่าพิเศษเฉพาะบางอย่างลงไปเพื่อสื่อความหมายพิเศษบางอย่าง เช่น ใส่ end date เป็น 31/12/9999 เพื่อแทนความหมายว่าไม่มีวันที่สิ้นสุด จะเห็นได้ว่าวันที่ 31/12/9999 ที่เราใส่ลงไปนั้น ไม่ได้ตั้งใจจะสื่อถึงวันที่ดังกล่าวอย่างแท้จริง แต่สื่อถึงวันที่เท่าไรก็ได้ที่ไกลออกไปมาก ๆ เท่านั้น ในกรณีดังกล่าวเราอาจใช้ค่า Null แทนได้ (และผมเคยได้รับคำแนะนำมาว่าใช้ค่า Null ดีกว่าในแง่ของ Optimizer statistics ซึ่งมีผลโดยตรงต่อ performance)

คุณสมบัติ

โดยปกติแล้ว ค่า Null เป็นค่าที่มีความหมายพิเศษ และจะไม่สามารถเปรียบเทียบกับใครได้เลยแม้กระทั่งตัวมันเอง ตรงจุดนี้ ผู้ที่เริ่มต้นเรียน SQL ใหม่ ๆ มักเกิดความสับสน ขอให้คิดง่าย ๆ ว่า Null คือ unknown หมายความว่ามันสามารถจะมีค่าเป็นอะไรก็ได้ทุกค่า แต่ปัจจุบันเรายังไม่รู้ค่าของมัน ดังนั้น ค่า Null เท่ากับ Null จึงไม่ถูกต้อง เพราะตัวหนึ่งอาจจะกลายเป็น a ส่วนอีกตัวอาจจะกลายเป็น b ไปก็ได้ และในขณะเดียวกัน Null ไม่เท่ากับ Null ก็ไม่ถูกต้องเช่นเดียวกัน เพราะมันอาจจะหมายถึง a ทั้งคู่ก็ได้

ตามทฤษฎีแล้ว Null มีความหมายไม่เหมือน 0 (Zero) เพราะ 0 มีค่า และ Null ต้องมีความหมายไม่เหมือนกัน empty string (string ที่มีขนาดเป็น 0 หรือใน SQL เราจะพิมพ์เครื่องหมายคำพูดสองตัวติดกัน ไม่เว้นวรรค) ด้วย ผมขอยกตัวอย่างง่าย ๆ สมมติผมเก็บ ที่อยู่ แยกคอลัมน์เป็น บ้านเลขที่ ซอย ถนน เขต แขวง จังหวัด ถ้าบ้านผมอยู่ติดถนนใหญ่ ผมก็ไม่มีชื่อซอย ในกรณีนี้ ค่าในคอลัมน์ซอย ควรเป็น empty string เพราะไม่ใช่ผมไม่รู้ชื่อซอย หรือไม่อยากระบุชื่อซอย แต่มันไม่มีชื่อซอยต่างหาก

แต่ทฤษฎีส่วนทฤษฎี ปัจจุบัน Oracle จัดการกับ Null และ empty String โดยมองว่ามันเป็นสิ่งเดียวกันเลย แม้จะมีการระบุว่าอนาคตอาจมีการเปลี่ยนแปลงกติกานี้ได้ และแนะนำให้เราอย่าใส่ empty string แทน null แต่ผมเห็นเขาระบุแบบนี้มาตั้งแต่เวอร์ชั่น 7 จนวันนี้เวอร์ชั่น 11 ก็ยังระบุข้อความเหมือนเดิม (แต่รู้สึกจะเน้นข้อความมากขึ้นอีกนิด) และก็นึกภาพไม่ออกจริง ๆ ว่าหากเกิดเปลี่ยนขึ้นมาเมื่อไร โลกของ application ที่สร้างบน oracle จะวุ่นวายสับสนขนาดไหน งานเข้าแน่ ๆ เอาเป็นว่า เราทำตามเขาว่าไปก่อน ก็แล้วกัน empty string ก็ส่วน empty string null ก็ส่วน null อย่าเอามาใช้ปนกัน

การคำนวณ

ตามกติกาแล้ว การคำนวณใด ๆ ที่ดำเนินการกับค่า Null ไม่ว่าจะเป็นการบวก ลบ คูณ หาร ผลลัพธ์ที่ได้จะต้องมีค่าเป็น Null ด้วย เพราะเรามองว่าค่า null จะหมายถึงค่าจริงอะไรก็ได้ ผลลัพธ์ที่ออกมาจะกลายเป็นอะไรก็ได้เหมือนกัน ดังนั้นมันก็ต้องเป็น Null อย่างไรก็ดี เนื่องจาก Oracle มอง Null เป็น empty String ทำให้การจับ null มาต่อกับ string อื่น (concatenation) ได้ค่าเท่ากับ string นั้น ๆ การ concatenation เป็น operator ยกเว้นตัวเดียวที่ทำงานกับ null แล้วไม่จำเป็นต้องได้ค่า null

เรื่องของการคำนวณนี้เองที่มักกลายเป็นข้อผิดพลาดของโปรแกรมเสมอ หากผู้พัฒนาลืมที่จะพิจารณาว่าค่าที่เอามาคำนวณมีโอกาสที่จะเป็น null หรือไม่ หลายคนที่มีประสบการณ์กับข้อผิดพลาดแบบนี้ ก็เลยถือเป็นกฏไปเลยก็มีว่าต้องใส่ NVL ครอบตลอด แต่ผมเองก็ไม่แนะนำการใช้เป็นกฏตายตัวแบบนี้ ทางที่ดี ต้องพิจารณาความเป็นไปได้ของข้อมูลก่อนถึงค่อยใส่ เพราะบางครั้งการได้ผลลัพธ์เป็น null อาจเป็นสิ่งที่ถูกต้อง เพราะจะเตือนเราว่าข้อมูลของเราเกิดข้อผิดพลาดขึ้น และสามารถกลับไปแก้ไขที่ข้อมูลต้นทางได้ ดีกว่าที่จะให้ข้อผิดพลาดนั้นถูกซ่อนไว้ภายใต้ function NVL

เรื่องของ Null ยังมีหัวข้อที่น่าสนใจอีกไม่น้อย ผมคิดว่าเราจะมาต่อกันครั้งหน้า ในเรื่องของการเปรียบเทียบ การจัดกลุ่ม และการเรียงลำดับ ครับ

References:
http://en.wikipedia.org/wiki/Null_(SQL
http://download.oracle.com/docs/cd/E11882_01/server.112/e10592/sql_elements005.htm#i59110

ข้อเขียนนี้ช่วยฉัน:  

2 comments:

Kim said...

พูดถึง Professor Codd ขอฝาก link paper ที่ท่านได้เขียนไว้เมื่อ 40 ปีที่แล้ว
จน Larry หยิบมา implement จนเป็น RDBMS ที่ใช้ในเชิงพาณิชย์เจ้าแรกของโลกครับ

http://portal.acm.org/citation.cfm?id=362685

Anonymous said...

เยี่ยมไปเลย...

Post a Comment