ในบางสถานการณ์ เราจำเป็นต้อง “อ่านข้อมูลจากหลายแถว” แล้วทำการประมวลผลทีละแถว เช่น
- คำนวณยอดสะสมรายบุคคล
- สร้างข้อความแจ้งเตือนเฉพาะลูกค้า
- ส่งอีเมลแต่ละรายการจาก queue
กรณีแบบนี้ เราสามารถใช้ Cursor ใน Stored Procedure เพื่อช่วยวนลูปข้อมูลจาก SELECT ทีละแถวได้
Cursor คืออะไร?
Cursor คือเครื่องมือใน MySQL สำหรับ “เลื่อนอ่าน” ข้อมูลจากผลลัพธ์ของ SELECT ทีละแถว ทำงานคล้ายกับการวนลูป array ในภาษาโปรแกรม เช่น foreach, for, while
โครงสร้างพื้นฐานของ Cursor
SQL
DECLARE cursor_name CURSOR FOR SELECT_statement;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cursor_name;
READ_LOOP: LOOP
FETCH cursor_name INTO var1, var2, ...;
IF done THEN
LEAVE READ_LOOP;
END IF;
-- คำสั่งที่ต้องการทำกับแต่ละแถว
END LOOP;
CLOSE cursor_name;ตัวอย่างการใช้งาน
คำนวณยอดรวมของลูกค้าทุกคน แล้วบันทึกลงตาราง customer_totals
สมมติว่าเรามีตารางชื่อ payments ซึ่งเก็บข้อมูลดังนี้
| customer_id | amount | status |
|---|---|---|
| 1 | 200.00 | paid |
| 2 | 150.00 | paid |
| 1 | 100.00 | paid |
| 3 | 80.00 | paid |
และมีตาราง customer_totals ซึ่งเก็บข้อมูลดังนี้
| customer_id | total_payment |
|---|---|
| 1 | 300.00 |
| 2 | 150.00 |
| 3 | 80.00 |
สร้าง Stored Procedure
SQL
DELIMITER //
CREATE PROCEDURE calculate_customer_totals()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE cid INT;
DECLARE total DECIMAL(10,2);
DECLARE cur CURSOR FOR
SELECT customer_id, SUM(amount)
FROM payments
WHERE status = 'paid'
GROUP BY customer_id;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
read_loop: LOOP
FETCH cur INTO cid, total;
IF done THEN
LEAVE read_loop;
END IF;
INSERT INTO customer_totals(customer_id, total_payment)
VALUES (cid, total);
END LOOP;
CLOSE cur;
END //
DELIMITER ;เรียกใช้งาน
SQL
CALL calculate_customer_totals();ผลลัพธ์
| customer_id | total_payment |
|---|---|
| 1 | 300.00 |
| 2 | 150.00 |
| 3 | 80.00 |
หมายเหตุ
- ต้องประกาศ
DECLARE CURSORและDECLARE HANDLERก่อนOPEN CONTINUE HANDLERช่วยป้องกัน error เมื่อFETCHแล้วไม่เจอข้อมูล- การลืมปิด Cursor (
CLOSE) อาจทำให้เกิด memory leak ในระบบที่ใช้บ่อย