Vòng lặp cho phép chạy một block code nhiều lần liên tiếp, miễn là điều kiện nào đó còn đúng hoặc khi đang lặp qua một tập dữ liệu nào đó. Cực kỳ tiện để tự động hóa task, xử lý dữ liệu lớn hoặc làm mấy thao tác lặp đi lặp lại.
Hãy tưởng tượng bạn phải tự tay phát kẹo cho từng đứa trẻ trong nhóm. Quá là hỗn loạn luôn! Thay vào đó, bạn chỉ cần đi qua danh sách bọn trẻ và phát từng đứa một viên kẹo. Trong trường hợp này, "đi qua danh sách" chính là vòng lặp đó.
PostgreSQL hỗ trợ mấy loại vòng lặp sau:
LOOP— vòng lặp đa năng, chạy cho đến khi bạn tự dừng nó.FOR— vòng lặp với số lần xác định, lặp qua một dải số hoặc kết quả truy vấn.WHILE— vòng lặp chạy khi điều kiện còn đúng.
Giờ mình sẽ giải thích từng loại nhé.
Vòng lặp vô hạn: LOOP
LOOP là dạng vòng lặp cơ bản nhất trong PL/pgSQL, cứ lặp mãi block code bên trong. Vòng này chạy vô hạn, bạn phải tự dừng nó bằng lệnh EXIT.
Cú pháp:
LOOP
-- Code của bạn ở đây
END LOOP;
Lấy ví dụ nhé. Tính tổng các số từ 1 đến 10 bằng LOOP:
DO $$
DECLARE
counter INT := 1;
sum INT := 0;
BEGIN
LOOP
sum := sum + counter; -- cộng giá trị hiện tại của biến đếm
counter := counter + 1; -- tăng biến đếm
-- Kết thúc vòng lặp nếu biến đếm lớn hơn 10
IF counter > 10 THEN
EXIT;
END IF;
END LOOP;
RAISE NOTICE 'Tổng các số từ 1 đến 10: %', sum;
END $$;
- Biến
countertăng lên mỗi lần lặp. - Điều kiện
IF counter > 10 THEN EXIT;sẽ dừng vòng lặp khi biến đếm vượt quá 10. - Cuối cùng sẽ in ra tổng các số.
Vòng lặp qua dải số hoặc tập dữ liệu: FOR
Loại vòng lặp thứ hai, FOR, dùng để lặp qua:
- Một dải số.
- Kết quả truy vấn SQL.
Vòng lặp qua dải số
Cú pháp:
FOR variable IN [REVERSE] start..end LOOP
-- Code của bạn ở đây
END LOOP;
In ra các số từ 1 đến 5:
DO $$
BEGIN
FOR i IN 1..5 LOOP
RAISE NOTICE 'Giá trị hiện tại: %', i;
END LOOP;
END $$;
Ví dụ với thứ tự ngược lại:
DO $$
BEGIN
FOR i IN REVERSE 5..1 LOOP
RAISE NOTICE 'Thứ tự ngược: %', i;
END LOOP;
END $$;
Vòng lặp qua kết quả truy vấn SQL
Kiểu này tiện để xử lý từng dòng trong bảng.
Cú pháp:
FOR variable IN QUERY (SELECT ...) LOOP
-- Code của bạn ở đây
END LOOP;
Lấy ví dụ lặp qua từng dòng trong bảng students:
DO $$
DECLARE
student_name TEXT;
BEGIN
FOR student_name IN QUERY (SELECT name FROM students) LOOP
RAISE NOTICE 'Xin chào, %!', student_name;
END LOOP;
END $$;
Vòng lặp có điều kiện: WHILE
WHILE sẽ chạy khi điều kiện cho trước còn đúng.
Cú pháp:
WHILE điều_kiện LOOP
-- Code của bạn ở đây
END LOOP;
Giờ mình tính tổng các số từ 1 đến 10 bằng WHILE nhé:
DO $$
DECLARE
counter INT := 1;
sum INT := 0;
BEGIN
WHILE counter <= 10 LOOP
sum := sum + counter;
counter := counter + 1;
END LOOP;
RAISE NOTICE 'Tổng các số từ 1 đến 10: %', sum;
END $$;
Ví dụ thực tế
Giờ khi đã hiểu cơ bản về vòng lặp, mình sẽ cho bạn xem vài ví dụ ứng dụng nhé.
Ví dụ 1: tạo bảng cửu chương
DO $$
DECLARE
i INT;
j INT;
BEGIN
FOR i IN 1..5 LOOP
FOR j IN 1..5 LOOP
RAISE NOTICE '% x % = %', i, j, i * j;
END LOOP;
END LOOP;
END $$;
Ví dụ 2: lặp qua kết quả truy vấn. Giả sử bạn có bảng products với các trường id và price. Cập nhật tất cả giá, tăng lên 10%.
DO $$
DECLARE
prod RECORD;
BEGIN
FOR prod IN QUERY (SELECT id, price FROM products) LOOP
UPDATE products
SET price = prod.price * 1.1
WHERE id = prod.id;
END LOOP;
END $$;
Lỗi thường gặp và cách tránh
Vòng lặp vô hạn trong LOOP hoặc WHILE. Nếu quên thêm điều kiện dừng (EXIT hoặc điều kiện đúng trong WHILE), vòng lặp sẽ chạy mãi không dừng. Kiểu như lái xe mà không có phanh ấy.
Lỗi khi lặp qua kết quả truy vấn. Nếu cấu trúc dữ liệu trả về không đúng như mong đợi, bạn sẽ gặp lỗi trong vòng lặp FOR IN QUERY.
Truy vấn không tối ưu trong vòng lặp. Ví dụ, chạy UPDATE trong mỗi lần lặp có thể làm chậm chương trình rất nhiều. Trong trường hợp này nên dùng một câu SQL duy nhất thay vì vòng lặp.
GO TO FULL VERSION