Oracle SQL scripts ที่สร้างโดยเอเจนต์โมเดลภาษาขนาดใหญ่ (LLM) อาจดูสมบูรณ์แบบในทางทฤษฎี แต่กลับสร้างปัญหาใหญ่ในระบบโปรดักชันได้ ในชุดโค้ดเก่า (legacy codebase) ขนาด 2.3 ล้านบรรทัด เอเจนต์ที่ขับเคลื่อนด้วย AI มักจะใส่ตัวระบุ (identifiers) ที่ไม่มีอยู่จริงลงไป เช่น การใช้ POLICY_STATUS แทนคอลัมน์ STATUS_CD ที่มีอยู่จริง หรือการอ้างถึงตาราง CUSTOMERS ที่ไม่มีอยู่แทนที่จะเป็น CUSTOMER

การรันสคริปต์เพื่อตรวจสอบคำผิดไม่ใช่ทางเลือกสำหรับคำสั่ง UPDATE หรือ DELETE เพราะการรันคำสั่งเหล่านี้บนชุดข้อมูลที่เหมือนโปรดักชันจะทำให้เกิดการล็อก (locks) การใช้เลข sequence และอาจส่งผลกระทบต่อเนื่อง (cascading side-effects) นักพัฒนาจึงต้องการวิธีตรวจสอบชื่อและไวยากรณ์โดยไม่แตะต้องข้อมูลใดๆ คำตอบที่เรียบง่ายอย่างไม่น่าเชื่อคือคำสั่ง EXPLAIN PLAN ของ Oracle ซึ่งสามารถนำมาประยุกต์ใช้เป็นขั้นตอนการทำ linting ได้

วิธีที่ EXPLAIN PLAN ทำหน้าที่เป็นตัวตรวจสอบความถูกต้องที่รวดเร็ว

เมื่อ Oracle ได้รับคำสั่ง มันจะเริ่มจากการทำ parsing ก่อน ซึ่งการ parsing จะตรวจสอบว่าตาราง คอลัมน์ และสิทธิ์ (privilege) ที่อ้างถึงทั้งหมดนั้นมีอยู่จริงหรือไม่ จากนั้นจึงสร้างแผนการประมวลผล (execution plan) และเขียนแผนนั้นลงในตารางระบบ คำสั่งนี้จะไม่รันคำสั่ง SQL จริงๆ เลย: จะไม่มีแถวข้อมูลใดถูกแก้ไข ไม่มีการทำงานของ trigger และไม่มีการล็อกข้อมูล หากตัว parser พบออบเจกต์ที่ไม่รู้จัก มันจะแจ้งข้อผิดพลาดภายในเวลาเพียงไม่กี่มิลลิวินาที

พฤติกรรมนี้ทำให้ EXPLAIN PLAN เป็นการตรวจสอบความพร้อม (pre-flight check) ที่สมบูรณ์แบบสำหรับ SQL ที่สร้างโดย AI หากพบตารางหรือคอลัมน์ที่ขาดหายไป ระบบจะรายงานทันที ช่วยให้ลูปการสร้าง (generation loop) สามารถแก้ไขข้อผิดพลาดได้ก่อนที่มนุษย์จะได้เห็นสคริปต์นั้นเสียอีก

เวิร์กโฟลว์ที่ผมนำไปใช้ใน CI pipeline ของผม

  1. แยก (Split) สคริปต์ที่เข้ามาออกเป็นคำสั่งย่อยๆ
  2. รัน (Run) EXPLAIN PLAN FOR <statement> กับ development schema
  3. รวบรวม (Collect) ข้อผิดพลาดจากการ parsing ที่ Oracle ส่งกลับมา
  4. ส่ง (Feed) ข้อผิดพลาดกลับไปยัง LLM เพื่อให้ลองใหม่อีกครั้ง

ในทางปฏิบัติ การลองใหม่เพียงครั้งเดียวก็สามารถแก้ไขข้อผิดพลาดด้านการตั้งชื่อส่วนใหญ่ได้ AI จะเรียนรู้ schema ที่ถูกต้องและปรับผลลัพธ์โดยอัตโนมัติ นอกจากนี้ ผมยังจำกัดสิทธิ์เอเจนต์ให้อยู่ในโหมดอ่านอย่างเดียว (read-only mode) โดยมันสามารถใช้คำสั่ง SELECT และ EXPLAIN PLAN ได้ แต่จะถูกบล็อกคำสั่ง DDL, DML และ COMMIT การสร้าง sandbox แบบนี้ช่วยรับประกันว่าฐานข้อมูลจะไม่ถูกแตะต้องในขณะที่ AI กำลังตรวจสอบโครงสร้าง

นอกจากการตรวจสอบชื่อแล้ว แผนที่สร้างขึ้นยังเผยให้เห็นสัญญาณเตือนด้านประสิทธิภาพ (performance red flags) ที่ชัดเจน หากคำสั่งใดจะทำให้เกิดการสแกนทั้งตาราง (full-table scan) บนตารางขนาดใหญ่ แผนจะแสดงให้เห็นก่อนที่จะมีการแตะต้องแถวข้อมูลใดๆ ช่วยให้นักพัฒนามีโอกาสเสนอให้สร้าง index หรือเขียนเงื่อนไข (predicate) ใหม่

ข้อจำกัดของแนวทางนี้

  • ไม่สามารถตรวจสอบความถูกต้องทางตรรกะได้ (Logical correctness isn’t verified): คำสั่งที่อ้างถึงคอลัมน์ที่ถูกต้องแต่ใช้ตัวกรอง (filter) ที่ผิดจะยังคงผ่านการทำ lint
  • ไม่ครอบคลุมบล็อก PL/SQL: ตัว parser จัดการได้เฉพาะคำสั่ง SQL แยกเป็นรายคำสั่งเท่านั้น ส่วนโค้ดเชิงกระบวนการ (procedural code) จำเป็นต้องมีเส้นทางการตรวจสอบแยกต่างหาก
  • ขาดการตรวจสอบในระดับข้อมูล: การทำ lint ไม่สามารถบอกคุณได้ว่าค่า literal นั้นตรงตามโดเมนของคอลัมน์หรือไม่ หรือการอ้างอิง foreign-key นั้นมีอยู่จริงหรือไม่
  • ใช้ได้เฉพาะ dev schema เท่านั้น: ข้อผิดพลาดที่จะปรากฏเฉพาะในโปรดักชันเท่านั้น เช่น ตารางที่มีอยู่ใน dev แต่ถูกเปลี่ยนชื่อใน prod จะยังคงมองไม่เห็นจนกว่าจะถึงภายหลัง

ช่องว่างเหล่านี้ไม่ได้ลดทอนประโยชน์ของวิธีการนี้ แต่มันเพียงแค่กำหนดขอบเขตการใช้งานเท่านั้น สำหรับสคริปต์ DML ส่วนใหญ่ที่สร้างโดย LLM รูปแบบความล้มเหลวที่พบบ่อยที่สุดคือการพิมพ์ผิดหรือการใช้ชื่อออบเจกต์ที่ผิด และนั่นคือสิ่งที่ EXPLAIN PLAN ตรวจจับได้พอดี

การนำไปใช้กับฐานข้อมูลอื่น (Portability)

หลักการเดียวกันนี้สามารถนำไปใช้กับฐานข้อมูลอื่นได้นอกเหนือจาก Oracle เช่น คำสั่ง PREPARE หรือ EXPLAIN ใน PostgreSQL สามารถพาร์สคิวรีได้โดยไม่ต้องรันจริง ส่วน SQL Server มี SET PARSEONLY ON ซึ่งบังคับให้ engine ตรวจสอบไวยากรณ์และชื่อออบเจกต์ในขณะที่ข้ามขั้นตอนการประมวลผลจริง RDBMS ใดก็ตามที่แยกการ parsing ออกจากการประมวลผลสามารถนำมาใช้เป็นด่านตรวจ linting ที่มีน้ำหนักเบาได้

บทสรุป

การรัน EXPLAIN PLAN (หรือสิ่งที่เทียบเท่ากัน) กับทุกคำสั่ง SQL ที่สร้างโดย AI จะเปลี่ยน database parser ให้กลายเป็นด่านตรวจ linting ที่ราคาถูกและไม่มีความเสี่ยง มันช่วยดักจับข้อผิดพลาดด้านการตั้งชื่อและไวยากรณ์ที่พบบ่อยที่สุดก่อนที่จะมีการเคลื่อนย้ายข้อมูลใดๆ ช่วยให้ระบบเก่า (legacy systems) มีความเสถียร ในขณะที่ยังช่วยให้นักพัฒนาได้รับประโยชน์จากประสิทธิภาพการทำงานที่เพิ่มขึ้นจากการเขียนโค้ดโดยมี LLM ช่วยเหลือ