Đôi khi mọi thứ trong vòng lặp đều ổn — cho đến khi có lý do để dừng sớm hơn dự kiến. May mắn là, PL/pgSQL cho tụi mình mấy công cụ tiện để kiểm soát chuyện này.
Dừng vòng lặp với EXIT
Đôi lúc bạn cần kết thúc vòng lặp trước khi nó tự chạy hết. Ví dụ như khi tìm thấy giá trị cần thiết, phát hiện lỗi hoặc kết thúc lặp theo điều kiện nào đó. Trong mấy trường hợp này, tụi mình dùng EXIT.
EXIT giống như kiểu "Thôi đủ rồi, mày làm xong việc rồi, dừng lại đi".
Cú pháp EXIT siêu đơn giản luôn:
EXIT WHEN điều_kiện;
Ở đây cụm WHEN chỉ ra khi nào thì vòng lặp sẽ dừng. Nếu không ghi điều kiện, EXIT sẽ dừng vòng lặp ngay lập tức.
Ví dụ: dừng vòng lặp khi tìm thấy giá trị cần thiết
Giả sử tụi mình có một cột chứa số hiệu sinh viên, và muốn tìm sinh viên có id cụ thể. Khi tìm thấy rồi thì dừng vòng lặp luôn.
DO $$
DECLARE
student_id INT;
BEGIN
FOR student_id IN 1..100 LOOP
RAISE NOTICE 'Đang kiểm tra student ID: %', student_id;
-- Nếu tìm thấy sinh viên cần, dừng vòng lặp
IF student_id = 42 THEN
RAISE NOTICE 'Đã tìm thấy student có ID 42!';
EXIT;
END IF;
END LOOP;
END $$;
Trong ví dụ này, vòng lặp duyệt qua các số từ 1 đến 100, kiểm tra từng "sinh viên". Khi gặp ID 42, nó in ra thông báo và dừng vòng lặp.
Bỏ qua lần lặp với CONTINUE
Có lúc trong vòng lặp bạn muốn bỏ qua một số lần lặp nhất định, nhưng vẫn tiếp tục các lần lặp tiếp theo. Cái này cực kỳ hữu ích nếu bạn muốn bỏ qua mấy dữ liệu "không cần thiết" hoặc skip bước cho điều kiện đặc biệt.
CONTINUE kiểu như: "Ờ, điều kiện này không ổn, thôi chuyển sang lần lặp tiếp theo đi".
CONTINUE hoạt động giống EXIT, nhưng thay vì dừng vòng lặp thì nó chỉ bỏ qua lần lặp hiện tại:
CONTINUE WHEN điều_kiện;
Nếu điều kiện đúng, lần lặp hiện tại sẽ kết thúc và chuyển sang lần tiếp theo.
Ví dụ: bỏ qua số chẵn
Ví dụ này tụi mình sẽ duyệt các số từ 1 đến 10, nhưng bỏ qua số chẵn, chỉ in ra số lẻ thôi.
DO $$
DECLARE
num INT;
BEGIN
FOR num IN 1..10 LOOP
-- Bỏ qua số chẵn
IF num % 2 = 0 THEN
CONTINUE;
END IF;
RAISE NOTICE 'Số lẻ: %', num;
END LOOP;
END $$;
Ở đây CONTINUE sẽ bỏ qua tất cả lần lặp mà num % 2 = 0 (tức là số chẵn). Kết quả là log chỉ in ra số lẻ thôi.
Kết hợp EXIT và CONTINUE
EXIT và CONTINUE có thể dùng chung để điều khiển vòng lặp linh hoạt hơn. Ví dụ, bạn có thể bỏ qua mấy lần lặp không cần thiết bằng CONTINUE, nhưng dừng hẳn vòng lặp nếu gặp cái gì đó quan trọng.
Ví dụ dưới đây, tụi mình bỏ qua tất cả số chia hết cho 3, nhưng dừng vòng lặp khi gặp số 15.
DO $$
DECLARE
num INT;
BEGIN
FOR num IN 1..20 LOOP
-- Bỏ qua số chia hết cho 3
IF num % 3 = 0 THEN
CONTINUE;
END IF;
-- Dừng vòng lặp nếu số bằng 15
IF num = 15 THEN
RAISE NOTICE 'Dừng lại ở số %', num;
EXIT;
END IF;
RAISE NOTICE 'Số hiện tại: %', num;
END LOOP;
END $$;
Ở đây vòng lặp hoạt động như sau:
- Số chia hết cho 3 thì bỏ qua (
CONTINUE). - Nếu số bằng 15 thì dừng vòng lặp (
EXIT). - Các số còn lại sẽ được in ra.
Ví dụ nâng cao: bỏ qua dữ liệu không hợp lệ và dừng khi gặp lỗi nghiêm trọng
Giờ thử tưởng tượng một bài toán thực tế hơn. Tụi mình muốn xử lý danh sách sinh viên, kiểm tra dữ liệu của họ. Dữ liệu không hợp lệ thì bỏ qua, còn nếu gặp "lỗi nghiêm trọng" thì dừng xử lý luôn.
DO $$
DECLARE
student RECORD;
BEGIN
FOR student IN
SELECT * FROM students
LOOP
-- Bỏ qua bản ghi có dữ liệu không hợp lệ
IF student.name IS NULL THEN
RAISE NOTICE 'Bỏ qua student có ID %: Thiếu tên', student.id;
CONTINUE;
END IF;
-- Dừng vòng lặp khi gặp lỗi nghiêm trọng
IF student.status = 'ERROR' THEN
RAISE EXCEPTION 'Lỗi nghiêm trọng với student ID %', student.id;
EXIT; -- Dòng này thực ra dư vì RAISE EXCEPTION sẽ dừng luôn rồi.
END IF;
-- Xử lý bản ghi
RAISE NOTICE 'Đang xử lý student: %', student.name;
END LOOP;
END $$;
Trong ví dụ này CONTINUE giúp bỏ qua sinh viên thiếu tên, còn EXIT (cùng với RAISE EXCEPTION) sẽ dừng vòng lặp khi phát hiện lỗi nghiêm trọng.
Mẹo thực tế và lỗi thường gặp
Đừng quên logic điều kiện. Dùng sai biểu thức điều kiện trong EXIT WHEN hoặc CONTINUE WHEN có thể làm vòng lặp chạy không như ý. Ví dụ, vòng lặp có thể dừng sớm hoặc bỏ qua dữ liệu quan trọng.
Lạm dụng CONTINUE. Nếu code của bạn toàn kiểm tra rồi CONTINUE, có thể nên xem lại logic vòng lặp cho đơn giản hơn.
Đừng nhầm EXIT với RETURN. EXIT chỉ dừng vòng lặp hiện tại, còn RETURN là dừng luôn cả function.
Cẩn thận với vòng lặp vô hạn. Nếu bạn dùng vòng lặp LOOP mà không có điều kiện dừng rõ ràng, quên EXIT là vòng lặp chạy mãi luôn đó.
GO TO FULL VERSION