อาจารย์ตี๋ที่สอน Oracle
EN
← กลับไปหน้าบทความ
SQL

คอลัมน์เป็นข้อความ แต่เงื่อนไขเป็นตัวเลข: Oracle เปรียบเทียบอย่างไร?

TL;DR — สรุปสั้น

เลือกชนิดคอลัมน์และ bind value ให้ตรงกับความหมายของข้อมูล อย่าพึ่ง implicit conversion

หากคอลัมน์ในตารางถูกออกแบบให้เก็บข้อความ (VARCHAR2) แต่เวลาเขียน SQL เงื่อนไข WHERE กลับส่งค่าเทียบเป็นตัวเลข (NUMBER) คำสั่งที่เคยรันผ่านในวันแรกจะเกิดอะไรขึ้นเมื่อมีข้อมูลที่เป็นตัวอักษรอย่าง 'ABC' เพิ่มเข้ามาในตาราง? บทความนี้จะพามาทดลองดูผลลัพธ์จริงทั้งก่อนและหลังเพิ่มข้อมูลชุดใหม่กันครับ

Implicit Conversion (การแปลงชนิดข้อมูลอัตโนมัติ): คือกลไกที่ Oracle ทำการแปลงชนิดข้อมูลให้โดยอัตโนมัติระหว่างประมวลผลคำสั่ง เมื่อมีการนำข้อมูลประเภทข้อความมาเปรียบเทียบกับตัวเลข Oracle จะให้ลำดับความสำคัญ (Data Type Precedence) แก่ตัวเลขมากกว่า จึงพยายามแปลงค่าในคอลัมน์ข้อความให้เป็นตัวเลขก่อนทำการเปรียบเทียบ ซึ่งหากข้อมูลในตารางมีแถวที่ไม่สามารถแปลงเป็นตัวเลขได้ ก็จะส่งผลให้เกิด Error ขึ้นทันที


ข้อความ 100 แปลงเป็นตัวเลขได้ แต่ ABC แปลงไม่ได้

ภาพอธิบายแนวคิด: ข้อความ 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

ผลลัพธ์และข้อสังเกต:

  1. บน Oracle 26ai ระบบจะแจ้งเตือนข้อผิดพลาด ORA-01722 พร้อมระบุข้อมูลเชิงลึกเพิ่มเติมผ่าน ORA-03302 ชัดเจนว่าไม่สามารถแปลงค่า 'ABC' ในคอลัมน์ AMOUNT_TEXT ให้เป็นตัวเลขได้
  2. ปัญหานี้ไม่ใช่เพียงแค่ "หาข้อมูลไม่เจอ" แต่เป็นข้อผิดพลาดรุนแรงระดับการประมวลผล (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)

  1. การออกแบบ Data Type ให้ตรงกับวัตถุประสงค์:
    • หากคอลัมน์นั้นเป็นจำนวนเงิน หรือค่าที่ต้องนำไปคำนวณ ควรใช้ชนิดข้อมูล NUMBER ตั้งแต่ขั้นตอนออกแบบตาราง
    • หากเป็นรหัสที่ต้องคงรูปศูนย์นำหน้าไว้ เช่น '00100' ให้ใช้ VARCHAR2 ทั้งการจัดเก็บและการ Bind ค่าในแอปพลิเคชัน
    • การนำฟังก์ชัน TO_NUMBER() มาครอบคอลัมน์ในคำสั่ง SQL ไม่ใช่การแก้ปัญหาที่ถูกต้อง เพราะไม่ได้ช่วยซ่อมข้อมูลขยะอย่าง 'ABC' ในตารางให้ถูกต้องได้
  2. ผลกระทบด้าน Performance (Index Suppression): การปล่อยให้เกิด Implicit Conversion บนคอลัมน์ในเงื่อนไข WHERE จะส่งผลให้ Oracle ไม่สามารถใช้งาน Index ปกติ (Standard B-Tree Index) บนคอลัมน์นั้นได้ ทำให้ระบบต้องทำ Full Table Scan เสมอ
  3. เรื่อง NLS (National Language Support): การรับค่าตัวเลขในรูปแบบข้อความต้องระวังเรื่องการแปลงสัญลักษณ์ทศนิยมหรือเครื่องหมายจุลภาค ซึ่งขึ้นอยู่กับค่า NLS Setting ของแต่ละ Session/Database ด้วยครับ

ติดตามอ่านบทความเทคนิค Oracle อื่น ๆ เพิ่มเติมได้ที่ บทความ Oracle บน teeDBA.com

#Oracle implicit conversion#Oracle data type comparison#TO_NUMBER#ORA-01722#SQL index

บทความที่เกี่ยวข้อง