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

ตรวจข้อมูลในแอปแล้ว ยังต้องมี Constraint ในฐานข้อมูลไหม?

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

ใช้ app validation เพื่อสื่อสารกับผู้ใช้ และใช้ constraint คุมกฎข้อมูลที่ทุกช่องทางต้องทำตาม

ฟอร์มหน้าเว็บหรือแอปพลิเคชันตรวจความถูกต้องของข้อมูลแล้ว แต่ถ้ามี Script, Batch Job หรือโปรแกรมอื่นเขียนข้อมูลเข้าตารางเดียวกัน กฎธุรกิจเหล่านั้นยังคงอยู่ครอบคลุมหรือไม่? บทความนี้จะพามาทดลองใส่ Constraint ไว้ที่ตาราง แล้วรันคำสั่งเพื่อดูผลลัพธ์ทั้งกรณีที่ผ่าน และกรณีที่ฐานข้อมูลปฏิเสธการบันทึกข้อมูลครับ

Constraint คือกฎเหล็กที่ฐานข้อมูลบังคับใช้กับข้อมูลเสมอ ไม่ว่าคำสั่งนั้นจะส่งมาจากหน้าฟอร์ม, แอปพลิเคชัน, Batch Job หรือการสั่ง SQL โดยตรง


Constraint ตรวจข้อมูลที่ฐานข้อมูล ไม่ว่าคำสั่งจะมาจากแอปหรือ batch job

ภาพอธิบายแนวคิด: Constraint ตรวจข้อมูลที่ฐานข้อมูล ไม่ว่าคำสั่งจะมาจากแอปหรือ batch job

ขั้นที่ 1: สร้างกฎและเตรียมข้อมูลเริ่มต้น

รันชุดคำสั่งนี้ครั้งเดียวใน Schema ทดลอง (ใช้ชื่อตารางที่ยังไม่มีในระบบ):

CREATE TABLE customers_demo (
    customer_id NUMBER PRIMARY KEY
);

CREATE TABLE orders_demo (
    order_id NUMBER PRIMARY KEY,
    customer_id NUMBER NOT NULL REFERENCES customers_demo(customer_id),
    status VARCHAR2(10) NOT NULL CHECK (status IN ('NEW', 'PAID'))
);

INSERT INTO customers_demo VALUES (120);
INSERT INTO orders_demo VALUES (101, 120, 'NEW');
COMMIT;

คำอธิบายกฎในตาราง:

  • PRIMARY KEY: บังคับห้ามค่าซ้ำ และห้ามเป็น NULL
  • NOT NULL: บังคับต้องมีค่าเสมอ ห้ามเว้นว่าง
  • CHECK: จำกัดค่าข้อมูลเฉพาะที่กำหนดไว้เท่านั้น (ในที่นี้คือ 'NEW' หรือ 'PAID')
  • REFERENCES (Foreign Key): บังคับว่า customer_id ที่อ้างถึง ต้องมีตัวตนอยู่จริงในตารางลูกค้า (customers_demo)

ในตัวอย่างนี้ คำสั่งซื้อ 101 เป็นของลูกค้า 120 และมีสถานะเป็น 'NEW'


ขั้นที่ 2: ตรวจสอบแถวข้อมูลที่ผ่านกฎ

ทดลองคิวรีอ่านข้อมูลเพื่อยืนยันการบันทึก:

SELECT order_id, customer_id, status FROM orders_demo;
    ORDER_ID  CUSTOMER_ID STATUS
------------ ------------ ----------
         101          120 NEW

1 row selected.

คำสั่งซื้อ 101 อ้างอิงถึงลูกค้า 120 ซึ่งมีอยู่จริงในระบบ และใช้สถานะ 'NEW' ซึ่งถูกต้องตามกฎทุกข้อ ข้อมูลจึงถูกบันทึกสำเร็จ


ขั้นที่ 3: ทดสอบการละเมิดกฎแต่ละประเภท

กรณีที่ 1: ลองใส่สถานะที่ไม่ได้รับอนุญาต
ทดลองส่งสถานะ 'SHIPPED' ซึ่งไม่อยู่ในเงื่อนไขของ CHECK:

INSERT INTO orders_demo VALUES (102, 120, 'SHIPPED');
INSERT INTO orders_demo VALUES (102, 120, 'SHIPPED')
*
ERROR at line 1:
ORA-02290: check constraint (KB_MKT_LAB_20261014_R2.SYS_C008588) violated
Help: https://docs.oracle.com/error-help/db/ora-02290/

ผลลัพธ์: เกิดข้อผิดพลาด ORA-02290 ระบุว่า Check Constraint ถูกละเมิด (ชื่อ SYS_C008588 คือชื่อที่ฐานข้อมูลสร้างให้อัตโนมัติในการทดลองนี้)

กรณีที่ 2: ใช้สถานะที่ถูกต้อง แต่ระบุรหัสลูกค้าที่ไม่มีอยู่จริง
ทดลองส่งสถานะ 'NEW' แต่ใช้ customer_id เท่ากับ 999 ซึ่งไม่มีในตารางลูกค้า:

INSERT INTO orders_demo VALUES (103, 999, 'NEW');
INSERT INTO orders_demo VALUES (103, 999, 'NEW')
*
ERROR at line 1:
ORA-02291: integrity constraint (KB_MKT_LAB_20261014_R2.SYS_C008590) violated - parent key not found
Help: https://docs.oracle.com/error-help/db/ora-02291/

ผลลัพธ์: เกิดข้อผิดพลาด ORA-02291 ระบุว่าไม่พบ Parent Key แสดงว่าฐานข้อมูลแยกแยะข้อผิดพลาดของการละเมิดกฎแต่ละข้อได้อย่างชัดเจน


ขั้นที่ 4: ตรวจสอบความถูกต้องของข้อมูลในตาราง

รันคำสั่งคิวรีเพื่อตรวจสอบว่ามีแถวข้อมูลที่ไม่ถูกต้องหลุดเข้าไปในตารางหรือไม่:

SELECT order_id FROM orders_demo ORDER BY order_id;
    ORDER_ID
------------
         101

1 row selected.

เมื่อคิวรีดูข้อมูลจริง พบว่ามีเพียงคำสั่งซื้อ 101 เท่านั้น ข้อมูลที่ไม่ผ่านเงื่อนไขทั้งสองครั้งถูกปฏิเสธและไม่ได้ถูกเพิ่มลงในตารางเลยแม้แต่แถวเดียว การตรวจผลลัพธ์จริงในตารางเช่นนี้สำคัญมากครับ เพราะช่วยยืนยันสถานะข้อมูลที่แท้จริงได้แน่นอนกว่าการอ่านเพียงข้อความ Error


สรุปหลักการนำไปใช้งานจริง

  1. การแบ่งหน้าที่ (Defense in Depth): แอปพลิเคชันยังคงควรตรวจข้อมูลเบื้องต้นที่หน้าฟอร์มเพื่อแจ้งเตือนผู้ใช้งานด้วยข้อความที่เข้าใจง่าย ส่วน Database Constraint ทำหน้าที่เป็นปราการด่านสุดท้ายคอยคุมความถูกต้องของทุกช่องทางเข้าข้อมูล (Data Integrity)
  2. การตั้งชื่อ Constraint: ในการใช้งานจริง แนะนำให้ตั้งชื่อ Constraint เองเสมอ (เช่น CONSTRAINT chk_order_status CHECK...) แทนการปล่อยให้ระบบตั้งชื่ออัตโนมัติ เพื่อให้ง่ายต่อการวิเคราะห์ปัญหาในระบบ Production
  3. ตรวจสอบข้อมูลเก่าก่อนเพิ่ม Constraint: ก่อนเพิ่มกฎใหม่ลงในตารางจริง ควรตรวจสอบข้อมูลเดิมในระบบและยืนยันกฎทางธุรกิจ (Business Rules) ให้รอบคอบ หากตั้งเงื่อนไขแคบเกินไปอาจขัดขวางการทำงานของข้อมูลชุดที่ถูกต้องได้ครับ

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