ในฐานะนักพัฒนาระบบที่ต้องทำงานกับฐานข้อมูลขนาดใหญ่ การจัดการข้อผิดพลาด (Error Handling) ถือเป็นหัวใจสำคัญที่แยกแยะระหว่างแอปพลิเคชันที่เสถียรกับแอปพลิเคชันที่เปราะบาง แม้ว่าในโลกของ Web API เราจะคุ้นเคยกับการตรวจสอบ HTTP Status Code เช่น 404 หรือ 500 แต่ในระบบฐานข้อมูลอย่าง PostgreSQL นั้น การทำความเข้าใจความแตกต่างระหว่าง ‘ความล้มเหลวในการเชื่อมต่อ’ (Connection Failure) และ ‘ความล้มเหลวทางตรรกะของข้อมูล’ (Logical Failure) เป็นสิ่งสำคัญอย่างยิ่ง
ทำไม PostgreSQL ถึงไม่ ‘Throw Error’ สำหรับข้อมูลที่ไม่ถูกต้อง?
ในเชิงเทคนิคแล้ว, เมื่อเราเรียกใช้คำสั่ง SQL, PostgreSQL จะจัดการกับข้อผิดพลาดที่ระดับของ Transaction และ Schema การที่โค้ดของเราทำงานต่อไปได้แม้ว่าข้อมูลจะผิดพลาด (เช่น การพยายามเพิ่มข้อมูลที่มี Primary Key ซ้ำกัน หรือการละเมิด Foreign Key) ไม่ได้แปลว่าคำสั่งนั้นสำเร็จเสมอไป
การทำงานของ PostgreSQL ที่ดีคือการทำให้เราสามารถแยกแยะได้ว่าข้อผิดพลาดที่เกิดขึ้นนั้นเป็นข้อผิดพลาดที่เกิดจากระบบ (System Error) หรือข้อผิดพลาดที่เกิดจากตรรกะทางธุรกิจ (Business Logic Error) ซึ่งเราต้องจัดการด้วยกลไกที่เหมาะสม
กุญแจสำคัญ: การใช้ EXCEPTION Blocks และ SQLSTATE
แทนที่จะพึ่งพาการตรวจสอบสถานะโค้ดภายนอกอย่าง `response.ok` ใน Fetch API, PostgreSQL มีกลไกที่ทรงพลังกว่าคือการใช้บล็อก EXCEPTION ภายใน Transaction เพื่อดักจับข้อผิดพลาดที่เกิดขึ้นระหว่างการดำเนินการ SQL ได้โดยตรง
เมื่อเราทราบว่าการเรียกใช้คำสั่ง SQL อาจล้มเหลวเนื่องจากการละเมิดข้อจำกัด (Constraint Violation) เราจะต้องห่อหุ้มคำสั่งนั้นไว้ใน BEGIN...EXCEPTION...END เพื่อให้โค้ดส่วนที่เหลือสามารถทำงานต่อได้โดยไม่ทำให้ Transaction ทั้งหมดล้มเหลว
ตัวอย่างการจัดการ Unique Violation
สมมติว่าเราต้องการบันทึกข้อมูลผู้ใช้ แต่ต้องมั่นใจว่าถ้าข้อมูลนั้นซ้ำกัน เราจะไม่ให้ระบบ Crash แต่ให้บันทึก Log และดำเนินการต่อได้ นี่คือวิธีการใช้ EXCEPTION:
SQL
DO $$ BEGIN INSERT INTO users (user_id, username) VALUES (101, 'alice'); EXCEPTION WHEN unique_violation THEN RAISE NOTICE 'Error: Username already exists. Skipping insertion.'; END; $$ LANGUAGE plpgsql;
ในตัวอย่างนี้, หากการแทรกข้อมูลล้มเหลวเนื่องจาก Unique Constraint (ซึ่งเป็น Logical Error) ระบบจะไม่หยุดทำงาน แต่จะกระโดดเข้าสู่บล็อก WHEN unique_violation และดำเนินการตามคำสั่ง RAISE NOTICE แทน
บทสรุป: การออกแบบระบบที่ทนทาน
การจัดการข้อผิดพลาดใน PostgreSQL ไม่ใช่แค่การจับข้อผิดพลาด (Catching Errors) เท่านั้น แต่คือการออกแบบ Transaction ให้สามารถคาดการณ์และตอบสนองต่อความล้มเหลวที่อาจเกิดขึ้นได้ทั้งระดับการเชื่อมต่อและระดับตรรกะของข้อมูล การใช้ EXCEPTION Blocks และการทำความเข้าใจ SQLSTATE จะช่วยให้แอปพลิเคชันของคุณมีความทนทาน (Resilient) และสามารถจัดการกับข้อมูลที่ผิดพลาดได้อย่างสง่างามในระดับ Senior Architect