ในฐานะนักพัฒนาที่ต้องจัดการกับระบบที่มีความซับซ้อนสูง ไม่ว่าจะเป็นการทำ Pagination (แบ่งหน้า) หรือการกรองข้อมูล (Filtering) ด้วยเงื่อนไขหลากหลาย การเขียน SQL Query ที่มีประสิทธิภาพจึงเป็นหัวใจสำคัญของงาน Codeigniter Active Record เป็นเครื่องมือช่วยให้เราสร้างคิวรี่ได้ง่ายขึ้น แต่เมื่อต้องเจอกับสถานการณ์ที่ต้องใช้ส่วนประกอบของ Query ซ้ำกันหลายครั้ง เช่น การนับจำนวนข้อมูลทั้งหมด (Count) และการดึงข้อมูลจริง (Results) เราจำเป็นต้องใช้วิธีการ Query Reuse เพื่อลดความซ้ำซ้อนและเพิ่มประสิทธิภาพสูงสุด
💡 ทำไมต้อง Reused Active Record Query?
เมื่อเราต้องการทำ Pagination โดยทั่วไปแล้ว จะต้องรัน Query อย่างน้อยสองครั้ง: ครั้งแรกคือการนับจำนวนแถวทั้งหมด (SELECT COUNT(*)) และครั้งที่สองคือการดึงข้อมูลตามช่วงที่กำหนดด้วย LIMIT และ OFFSET
หากเราเขียนโค้ดโดยการสร้างเงื่อนไข WHERE หรือ JOIN ใหม่สำหรับทั้งสอง Query จะทำให้เกิดความเสี่ยงที่จะผิดพลาดและลดประสิทธิภาพในการทำงานของฐานข้อมูลอย่างมาก โดยเฉพาะเมื่อมีการเพิ่มเงื่อนไข Filter (เช่น สถานะ, วันที่) เข้ามาเรื่อยๆ
⚙️ หลักการทำงาน: การสร้างส่วนประกอบร่วม (Common Components)
แนวคิดหลักของการทำ Query Reuse คือการแยกส่วนของโค้ดหรือส่วนของ SQL ที่เป็น เงื่อนไขพื้นฐาน ออกมาก่อน จากนั้นจึงนำส่วนนี้ไปใช้กับ Query สุดท้ายทั้งสองตัว (Count และ Select) วิธีที่ Framework อย่าง CodeIgniter จัดการคือการใช้กลไก Cache ของ Active Record เพื่อให้ระบบสร้างโครงสร้าง Query ส่วนแรกเพียงครั้งเดียว
ในบริบทของ PostgreSQL ซึ่งเป็นฐานข้อมูลที่มีความสามารถสูง เรายังคงยึดหลักการนี้ในการเขียนโค้ด โดยเน้นที่การสร้างเงื่อนไข WHERE ที่ซับซ้อนให้เสร็จสมบูรณ์ก่อน แล้วจึงนำโครงสร้างนั้นไปใช้กับคำสั่ง COUNT() และ SELECT * LIMIT ...
ตัวอย่างการใช้งาน Query Reuse ใน PostgreSQL
สมมติว่าเรามีตาราง district และต้องการดึงข้อมูลพร้อมกรองตามเงื่อนไขที่ผู้ใช้กำหนด (เช่น province_id) เราจะสร้างโครงสร้างพื้นฐานของ Query ก่อน:
SQL
-- ส่วนที่ 1: การสร้าง Common WHERE Clause (จำลองการ Build Query Base) --
SELECT d.district_id, d.enable, d.district_code, d.district_name, p.province_name
FROM district AS d
LEFT JOIN province AS p ON d.province_id = p.province_id
WHERE 1=1
เมื่อได้โครงสร้างพื้นฐานนี้แล้ว เราสามารถนำไปใช้กับ Query ที่แตกต่างกัน:
1. Query สำหรับนับจำนวน (Record Count)
เราเพียงแค่เปลี่ยนคำสั่ง SELECT ให้เป็น COUNT() โดยที่เงื่อนไข WHERE ยังคงเดิม
SQL
SELECT COUNT(d.district_id) AS recordsTotal
FROM district AS d
LEFT JOIN province AS p ON d.province_id = p.province_id
WHERE 1=1 -- ส่วนนี้คือเงื่อนไขที่ถูก Reuse จากส่วนแรก
-- AND d.some_column = :filter_value;
2. Query สำหรับดึงข้อมูล (Results)
เราใช้โครงสร้างเดิม แต่เพิ่ม LIMIT และ OFFSET เข้าไปเพื่อจำกัดผลลัพธ์:
SQL
SELECT d.district_id, d.enable, d.district_code, d.district_name, p.province_name
FROM district AS d
LEFT JOIN province AS p ON d.province_id = p.province_id
WHERE 1=1 -- ส่วนนี้คือเงื่อนไขที่ถูก Reuse จากส่วนแรก
-- AND d.some_column = :filter_value;
LIMIT 10 OFFSET 0;
✅ สรุปและข้อดีทางสถาปัตยกรรม
การใช้เทคนิค Query Reuse ไม่ได้เป็นเพียงแค่การลดโค้ดที่ซ้ำกันเท่านั้น แต่ยังเป็นการออกแบบระบบที่มีความยืดหยุ่น (Maintainability) และประสิทธิภาพสูง (Performance) ในระดับ Senior Architect
- Consistency: มั่นใจได้ว่าเงื่อนไข Filter หรือ Join จะถูกใช้กับทั้ง Count และ Select อย่างถูกต้องเสมอ
- Efficiency: ลดภาระในการประมวลผลของ Active Record Layer ทำให้ Query Builder ทำงานได้อย่างมีประสิทธิภาพสูงสุด
การทำความเข้าใจรูปแบบนี้จะช่วยให้คุณสามารถเขียนโค้ดที่รองรับการขยายตัว (Scalability) ของระบบได้ในระยะยาว