คอลัมน์เป็นข้อความ แต่เงื่อนไขเป็นตัวเลข: Oracle เปรียบเทียบอย่างไร?
เลือกชนิดคอลัมน์และ bind value ให้ตรงกับความหมายของข้อมูล อย่าพึ่ง implicit conversion
หากคอลัมน์ในตารางถูกออกแบบให้เก็บข้อความ (VARCHAR2) แต่เวลาเขียน SQL เงื่อนไข WHERE กลับส่งค่าเทียบเป็นตัวเลข (NUMBER) คำสั่งที่เคยรันผ่านในวันแรกจะเกิดอะไรขึ้นเมื่อมีข้อมูลที่เป็นตัวอักษรอย่าง 'ABC' เพิ่มเข้ามาในตาราง? บทความนี้จะพามาทดลองดูผลลัพธ์จริงทั้งก่อนและหลังเพิ่มข้อมูลชุดใหม่กันครับ
Implicit Conversion (การแปลงชนิดข้อมูลอัตโนมัติ): คือกลไกที่ Oracle ทำการแปลงชนิดข้อมูลให้โดยอัตโนมัติระหว่างประมวลผลคำสั่ง เมื่อมีการนำข้อมูลประเภทข้อความมาเปรียบเทียบกับตัวเลข Oracle จะให้ลำดับความสำคัญ (Data Type Precedence) แก่ตัวเลขมากกว่า จึงพยายามแปลงค่าในคอลัมน์ข้อความให้เป็นตัวเลขก่อนทำการเปรียบเทียบ ซึ่งหากข้อมูลในตารางมีแถวที่ไม่สามารถแปลงเป็นตัวเลขได้ ก็จะส่งผลให้เกิด Error ขึ้นทันที

ภาพอธิบายแนวคิด: ข้อความ 100 แปลงเป็นตัวเลขได้ แต่ ABC แปลงไม่ได้
ขั้นที่ 1: เตรียมตารางและข้อมูลข้อความ
รันชุดคำสั่งนี้ใน Schema ทดลอง:
CREATE TABLE kb_amount_text (
amount_text VARCHAR2(10)
);
INSERT INTO kb_amount_text VALUES ('100');
INSERT INTO kb_amount_text VALUES ('250');
COMMIT;
แม้อักขระ '100' และ '250' จะดูเหมือนตัวเลข แต่ในระดับโครงสร้างตาราง คอลัมน์นี้เป็น VARCHAR2 และข้อมูลถูกบันทึกโดยมีเครื่องหมาย Quote กำกับไว้
ขั้นที่ 2: ทดลองเปรียบเทียบข้อความกับตัวเลข
ทดลองเขียนคำสั่ง WHERE โดยส่งค่าเทียบเป็นตัวเลข 100 (ไม่มี Quote กำกับ):
SELECT amount_text FROM kb_amount_text WHERE amount_text = 100;
AMOUNT_TEX
----------
100
1 row selected.
ผลลัพธ์: คำสั่งทำงานสำเร็จและคืนค่า '100' ออกมาได้ เนื่องจากข้อมูล '100' และ '250' ที่มีอยู่ในตารางขณะนั้น สามารถแปลงเป็นตัวเลขเพื่อเปรียบเทียบได้ทั้งหมด
(หมายเหตุ: หัวคอลัมน์ AMOUNT_TEX ที่ตัดสั้นลงมา เกิดจากการตั้งค่าฟอร์แมตการแสดงผลของ Client แต่ชื่อคอลัมน์จริงในฐานข้อมูลยังคงเป็น AMOUNT_TEXT)
ขั้นที่ 3: เพิ่มข้อมูลตัวอักษรที่ไม่สามารถแปลงเป็นตัวเลขได้
ทดลองแทรกข้อมูลข้อความ 'ABC' ลงในตาราง:
INSERT INTO kb_amount_text VALUES ('ABC');
COMMIT;
1 row created.
Commit complete.
จากนั้นลองรันคำสั่ง SELECT ด้วยเงื่อนไขเดิมอีกครั้ง:
SELECT amount_text FROM kb_amount_text WHERE amount_text = 100;
ERROR:
ORA-01722: unable to convert string value containing 'A' to a number: AMOUNT_TEXT
ORA-03302: (ORA-01722 details) invalid string value: ABC
Help: https://docs.oracle.com/error-help/db/ora-01722/
no rows selected
ผลลัพธ์และข้อสังเกต:
- บน Oracle 26ai ระบบจะแจ้งเตือนข้อผิดพลาด
ORA-01722พร้อมระบุข้อมูลเชิงลึกเพิ่มเติมผ่านORA-03302ชัดเจนว่าไม่สามารถแปลงค่า'ABC'ในคอลัมน์AMOUNT_TEXTให้เป็นตัวเลขได้ - ปัญหานี้ไม่ใช่เพียงแค่ "หาข้อมูลไม่เจอ" แต่เป็นข้อผิดพลาดรุนแรงระดับการประมวลผล (Execution Error) แม้ส่วนท้ายของ Output จะแสดง
no rows selectedแต่เราต้องสังเกตข้อความ Error ด้านบนก่อนเสมอ
ขั้นที่ 4:แก้ไขให้เปรียบเทียบข้อมูลชนิดเดียวกัน
ปรับแก้ไขคำสั่ง SQL โดยใส่เครื่องหมาย Quote ครอบค่าตัวเลข เพื่อให้เป็นการเปรียบเทียบระหว่างข้อความกับข้อความ (VARCHAR2 vs VARCHAR2):
SELECT amount_text FROM kb_amount_text WHERE amount_text = '100';
AMOUNT_TEX
----------
100
1 row selected.
ผลลัพธ์: คำสั่งคิวรีผ่านได้เรียบร้อยและคืนค่า '100' ออกมาอย่างถูกต้อง เนื่องจาก Oracle ทำการเปรียบเทียบข้อความกับข้อความโดยตรง จึงไม่ต้องพยายามแปลงค่า 'ABC' ให้เป็นตัวเลขอีกต่อไป
สรุปแนวทางปฏิบัติที่ดี (Best Practices)
- การออกแบบ Data Type ให้ตรงกับวัตถุประสงค์:
- หากคอลัมน์นั้นเป็นจำนวนเงิน หรือค่าที่ต้องนำไปคำนวณ ควรใช้ชนิดข้อมูล
NUMBERตั้งแต่ขั้นตอนออกแบบตาราง - หากเป็นรหัสที่ต้องคงรูปศูนย์นำหน้าไว้ เช่น
'00100'ให้ใช้VARCHAR2ทั้งการจัดเก็บและการ Bind ค่าในแอปพลิเคชัน - การนำฟังก์ชัน
TO_NUMBER()มาครอบคอลัมน์ในคำสั่ง SQL ไม่ใช่การแก้ปัญหาที่ถูกต้อง เพราะไม่ได้ช่วยซ่อมข้อมูลขยะอย่าง'ABC'ในตารางให้ถูกต้องได้
- หากคอลัมน์นั้นเป็นจำนวนเงิน หรือค่าที่ต้องนำไปคำนวณ ควรใช้ชนิดข้อมูล
- ผลกระทบด้าน Performance (Index Suppression): การปล่อยให้เกิด Implicit Conversion บนคอลัมน์ในเงื่อนไข
WHEREจะส่งผลให้ Oracle ไม่สามารถใช้งาน Index ปกติ (Standard B-Tree Index) บนคอลัมน์นั้นได้ ทำให้ระบบต้องทำ Full Table Scan เสมอ - เรื่อง NLS (National Language Support): การรับค่าตัวเลขในรูปแบบข้อความต้องระวังเรื่องการแปลงสัญลักษณ์ทศนิยมหรือเครื่องหมายจุลภาค ซึ่งขึ้นอยู่กับค่า NLS Setting ของแต่ละ Session/Database ด้วยครับ
ติดตามอ่านบทความเทคนิค Oracle อื่น ๆ เพิ่มเติมได้ที่ บทความ Oracle บน teeDBA.com
บทความที่เกี่ยวข้อง

ตรวจข้อมูลในแอปแล้ว ยังต้องมี Constraint ในฐานข้อมูลไหม?
เปรียบเทียบการตรวจข้อมูลในแอปกับ constraint ใน Oracle พร้อมตัวอย่างกฎที่ฐานข้อมูลบังคับใช้

วิธีทำเลขรันอัตโนมัติที่ไม่ต้องเขียน trigger ด้วยการใช้ Identity Column
Oracle 12c ขึ้นไปทำ auto-increment ได้ด้วย Identity Column ไม่ต้องสร้าง sequence + trigger เอง — วิธีใช้ 3 โหมด พร้อมข้อควรระวังตอน migrate

Bind Variable คืออะไร ทำไมควรแยกค่าออกจากข้อความ SQL?
ทดลองใช้ bind variable กับ SQL สั้น ๆ แล้วดูเหตุผลเรื่อง cursor reuse, parse และขอบเขตด้านความปลอดภัย