SQL
-- 1. ประกาศ Cursor เพื่อจำกัดการดึงข้อมูลเป็นชุดย่อย<br>DECLARE data_cursor CURSOR FOR SELECT id, name, value FROM large_dataset WHERE status = 'active';<br><br>-- 2. เปิด Cursor และเริ่มดึงข้อมูลเป็นชุด (Batching)<br>OPEN data_cursor;<br>FETCH data_cursor INTO batch_data LIMIT 100; -- ดึงมาทีละ 100 แถว<br>-- ใน Backend จะมีการวนลูป (LOOP) เพื่อดึงข้อมูลชุดถัดไปจนกว่าจะหมด<br>-- ตัวอย่างการส่งผลลัพธ์เป็น JSON Array (100 records) ที่ Backend จะรับไปต่อ<br>SELECT json_agg(t) FROM (VALUES ('1,2,3'), ('4,5,6')) AS t(data_chunk);<br>-- 3. ปิด Cursor เมื่อเสร็จสิ้น<br>CLOSE data_cursor;
💻 การรับ Stream ด้วย jQuery Ajax
“>แทนที่จะใช้ $.ajax แบบปกติที่รอ Response ทั้งหมด เราต้องใช้ XMLHttpRequest (XHR) โดยตรงเพื่อจัดการ Event ของการรับข้อมูลเป็นก้อน (Chunk) ซึ่งเป็นหัวใจของการ Streaming:
JavaScript
// 1. สร้าง XHR Object<br>const xhr = new XMLHttpRequest();<br><br>// 2. กำหนด Event Handler เมื่อได้รับข้อมูล<br>xhr.onload = function() {<br> // ข้อมูลทั้งหมดถูกรับมาแล้ว (กรณีที่เซิร์ฟเวอร์ปิด Stream)<br> console.log('Stream Complete:', this.responseText);<br> // นำข้อมูลสุดท้ายไปแสดงผล<br> document.getElementById('results').innerHTML = this.responseText;<br>};<br><br>// 3. กำหนด Event Handler เมื่อได้รับข้อมูลเป็นชิ้น (Chunk) - สำคัญที่สุด<br>xhr.onprogress = function(event) {<br> if (event.lengthComputable) {<br> const loaded = event.loaded; // ขนาดข้อมูลที่โหลดมาแล้ว<br> const total = event.total; // ขนาดรวม<br> console.log('Progress:', Math.round((loaded / total) * 100) + '%');<br> // ในความเป็นจริง: ต้องมีการ Parse ข้อมูลที่เข้ามาใน Chunk นี้และอัปเดต DOM<br> // เช่น: displayChunk(event.data);<br> }<br>};<br><br>// 4. เปิดการเชื่อมต่อและส่งคำขอ<br>xhr.open('GET', '/api/stream-data', true);<br>xhr.send();
การใช้ onprogress คือกุญแจสำคัญที่ทำให้ Ajax สามารถรับข้อมูลแบบ Stream ได้อย่างแท้จริง ทำให้ UI ไม่ค้างและผู้ใช้เห็นการอัปเดตข้อมูลแบบ Real-time
✨ บทสรุปสำหรับ Senior Architect
“>การทำ Streaming Data ไม่ใช่แค่การลดเวลาการโหลด แต่เป็นการปรับปรุง User Experience (UX) และ System Performance ในการจัดการ I/O ที่มีขนาดใหญ่ การออกแบบระบบที่ถูกต้องต้องเริ่มตั้งแต่การจัดการทรัพยากรที่ PostgreSQL (ใช้ Cursors) การส่งข้อมูลที่ Backend (ใช้ Streaming Response) และการรับข้อมูลที่ Client (ใช้ XHR onprogress) เพื่อให้มั่นใจว่าข้อมูลจะถูกประมวลผลและแสดงผลอย่างต่อเนื่อง ไม่ใช่การรอจนกว่างานทั้งหมดจะเสร็จสมบูรณ์
ในโลกของการพัฒนาเว็บสมัยใหม่ การที่ระบบต้องประมวลผลข้อมูลขนาดใหญ่ หรือต้องรอการทำงานที่ใช้เวลานาน (เช่น การ Export รายงาน, การประมวลผลข้อมูลจำนวนมาก) เป็นเรื่องที่เกิดขึ้นเป็นประจำ หากเราปล่อยให้ผู้ใช้รอหน้าจอโหลดอย่างเดียว ประสบการณ์ผู้ใช้ (UX) จะลดลงอย่างมาก
“>ทางแก้ไขคือการใช้เทคนิค Streaming Data หรือการรับข้อมูลแบบสตรีมมิ่ง (Stream) ซึ่งหมายถึงการที่เซิร์ฟเวอร์ไม่รอจนกว่างานจะเสร็จสมบูรณ์ แต่จะค่อย ๆ ผลัก (Push) ข้อมูลออกมาให้ไคลเอนต์รับทราบเป็นชิ้น ๆ ทันทีที่พร้อม ทำให้ผู้ใช้รู้สึกว่าระบบกำลังทำงานอย่างต่อเนื่องและรวดเร็ว
💡 หลักการทำงาน: Streaming Data จาก PostgreSQL ผ่าน Ajax
“>เมื่อเราต้องการนำข้อมูลจำนวนมากจากฐานข้อมูล PostgreSQL มาแสดงผลแบบสตรีมมิ่ง เราต้องเข้าใจกระบวนการตั้งแต่ต้นทางถึงปลายทางอย่างลึกซึ้ง โดยมีองค์ประกอบหลัก 3 ส่วน:
- PostgreSQL Layer (Source): ฐานข้อมูลต้องถูกเรียกใช้ในลักษณะที่สามารถส่งผลลัพธ์ออกมาเป็นชุดย่อย ๆ ได้อย่างต่อเนื่อง (เช่น การใช้ Cursors หรือการแบ่ง Batching) เพื่อไม่ให้เกิดการโหลดหน่วยความจำสูงเกินไป
- Backend Layer (Bridge): ภาษาฝั่งเซิร์ฟเวอร์ (เช่น Node.js, PHP) จะทำหน้าที่เป็นตัวกลางในการเชื่อมต่อกับ PostgreSQL และรับข้อมูลมาทีละส่วน จากนั้นจึงส่งข้อมูลส่วนนั้นออกไปในรูปแบบ Response Stream
- Client Layer (Consumer): ใช้ jQuery Ajax (โดยเฉพาะการจัดการ XMLHttpRequest) เพื่อรับข้อมูลที่ถูกส่งมาเป็นชิ้น ๆ (Chunk by Chunk) และนำไปอัปเดต DOM ทันที
⚙️ ตัวอย่างการจัดการข้อมูลแบบ Stream ใน PostgreSQL
“>ในระดับ PostgreSQL เราจะใช้ DECLARE CURSOR เพื่อให้การดึงข้อมูลเป็นไปแบบ Iterative และจัดการหน่วยความจำได้ดี:
SQL
-- 1. ประกาศ Cursor เพื่อจำกัดการดึงข้อมูลเป็นชุดย่อย<br>DECLARE data_cursor CURSOR FOR SELECT id, name, value FROM large_dataset WHERE status = 'active';<br><br>-- 2. เปิด Cursor และเริ่มดึงข้อมูลเป็นชุด (Batching)<br>OPEN data_cursor;<br>FETCH data_cursor INTO batch_data LIMIT 100; -- ดึงมาทีละ 100 แถว<br>-- ใน Backend จะมีการวนลูป (LOOP) เพื่อดึงข้อมูลชุดถัดไปจนกว่าจะหมด<br>-- ตัวอย่างการส่งผลลัพธ์เป็น JSON Array (100 records) ที่ Backend จะรับไปต่อ<br>SELECT json_agg(t) FROM (VALUES ('1,2,3'), ('4,5,6')) AS t(data_chunk);<br>-- 3. ปิด Cursor เมื่อเสร็จสิ้น<br>CLOSE data_cursor;
💻 การรับ Stream ด้วย jQuery Ajax
“>แทนที่จะใช้ $.ajax แบบปกติที่รอ Response ทั้งหมด เราต้องใช้ XMLHttpRequest (XHR) โดยตรงเพื่อจัดการ Event ของการรับข้อมูลเป็นก้อน (Chunk) ซึ่งเป็นหัวใจของการ Streaming:
JavaScript
// 1. สร้าง XHR Object<br>const xhr = new XMLHttpRequest();<br><br>// 2. กำหนด Event Handler เมื่อได้รับข้อมูล<br>xhr.onload = function() {<br> // ข้อมูลทั้งหมดถูกรับมาแล้ว (กรณีที่เซิร์ฟเวอร์ปิด Stream)<br> console.log('Stream Complete:', this.responseText);<br> // นำข้อมูลสุดท้ายไปแสดงผล<br> document.getElementById('results').innerHTML = this.responseText;<br>};<br><br>// 3. กำหนด Event Handler เมื่อได้รับข้อมูลเป็นชิ้น (Chunk) - สำคัญที่สุด<br>xhr.onprogress = function(event) {<br> if (event.lengthComputable) {<br> const loaded = event.loaded; // ขนาดข้อมูลที่โหลดมาแล้ว<br> const total = event.total; // ขนาดรวม<br> console.log('Progress:', Math.round((loaded / total) * 100) + '%');<br> // ในความเป็นจริง: ต้องมีการ Parse ข้อมูลที่เข้ามาใน Chunk นี้และอัปเดต DOM<br> // เช่น: displayChunk(event.data);<br> }<br>};<br><br>// 4. เปิดการเชื่อมต่อและส่งคำขอ<br>xhr.open('GET', '/api/stream-data', true);<br>xhr.send();
“>การใช้ onprogress คือกุญแจสำคัญที่ทำให้ Ajax สามารถรับข้อมูลแบบ Stream ได้อย่างแท้จริง ทำให้ UI ไม่ค้างและผู้ใช้เห็นการอัปเดตข้อมูลแบบ Real-time
✨ บทสรุปสำหรับ Senior Architect
“>การทำ Streaming Data ไม่ใช่แค่การลดเวลาการโหลด แต่เป็นการปรับปรุง User Experience (UX) และ System Performance ในการจัดการ I/O ที่มีขนาดใหญ่ การออกแบบระบบที่ถูกต้องต้องเริ่มตั้งแต่การจัดการทรัพยากรที่ PostgreSQL (ใช้ Cursors) การส่งข้อมูลที่ Backend (ใช้ Streaming Response) และการรับข้อมูลที่ Client (ใช้ XHR onprogress) เพื่อให้มั่นใจว่าข้อมูลจะถูกประมวลผลและแสดงผลอย่างต่อเนื่อง ไม่ใช่การรอจนกว่างานทั้งหมดจะเสร็จสมบูรณ์