Cùng đào sâu hơn vào PL/pgSQL và bắt đầu dùng nó một cách chủ động nha.
Block code
Block code trong PL/pgSQL là thành phần xây dựng chính của ngôn ngữ này. Có thể nói nó là khung xương mà trên đó các function, procedure và các phép thuật khác của chúng ta được dựng lên. Block đảm bảo việc thực thi logic, xử lý dữ liệu, quản lý lỗi, tất cả gói gọn trong một "container".
Các block PL/pgSQL được cấu trúc và gồm ba phần chính:
DECLARE: khai báo biến (có thể có hoặc không).BEGIN ... END: block thực thi chính, nơi logic được thực hiện.EXCEPTION: xử lý lỗi (có thể có hoặc không).
Cho bạn nào thích ví von: tưởng tượng bạn đang đọc công thức nấu ăn. Dù công thức có thể bắt đầu bằng danh sách nguyên liệu, nhưng phép thuật thực sự xảy ra trong quá trình nấu nướng. Theo ngôn ngữ PL/pgSQL:
DECLARE— là danh sách nguyên liệu (biến).BEGIN ... END— là nơi trộn, chiên, luộc các thứ.EXCEPTION— là kế hoạch dự phòng nếu món ăn bị cháy khét.
Cú pháp block PL/pgSQL
Đầu tiên, hãy xem qua cấu trúc tổng thể của block, như một "bộ xương". Sau đó sẽ thêm thịt (hoặc phô mai chay nếu bạn thích) — tức là logic cụ thể.
DO $$
DECLARE
-- Ở đây khai báo biến
student_count INT;
BEGIN
-- Ở đây thực hiện logic
SELECT COUNT(*) INTO student_count FROM students;
RAISE NOTICE 'Tổng số sinh viên: %', student_count;
EXCEPTION
-- Ở đây xử lý lỗi
WHEN OTHERS THEN
RAISE NOTICE 'Đã xảy ra lỗi.';
END;
$$;
Cùng phân tích từng bước nhé.
DECLARE— chỗ này mình khai báo các biến. Điều hay là PL/pgSQL hỗ trợ gần như tất cả các kiểu dữ liệu có trong PostgreSQL — từINTEGERđơn giản đến JSONB "dị". Để khai báo biến, bạn chỉ cần ghi tên, kiểu dữ liệu và nếu muốn thì gán giá trị khởi tạo luôn.
Ví dụ:
DECLARE
student_name TEXT; -- Biến cho tên sinh viên
course_count INT := 0; -- Gán giá trị khởi tạo là 0
is_graduated BOOLEAN; -- Biến logic
Lưu ý là biến có thể được khởi tạo giá trị (như course_count) hoặc không.
BEGIN ... END— block thực thi chính.
Phần này chịu trách nhiệm thực hiện logic chính. Ở đây bạn có thể:
- Chạy các truy vấn SQL (
SELECT,INSERT, v.v.). - Xử lý dữ liệu.
- Dùng các cấu trúc điều khiển (
IF,LOOP, v.v.). - In thông báo debug bằng
RAISE.
Ví dụ:
BEGIN
SELECT COUNT(*) INTO student_count FROM students;
IF student_count > 0 THEN
RAISE NOTICE 'Có sinh viên!';
ELSE
RAISE NOTICE 'Không tìm thấy sinh viên nào.';
END IF;
END;
EXCEPTION— xử lý lỗi (có thể có hoặc không).
Nếu trong quá trình thực thi block có lỗi xảy ra, phần EXCEPTION cho phép bạn bắt lỗi và làm gì đó hữu ích — ví dụ như in thông báo hoặc chạy code thay thế.
Ví dụ:
BEGIN
SELECT COUNT(*) INTO student_count FROM non_existing_table; -- Lỗi!
EXCEPTION
WHEN OTHERS THEN
RAISE NOTICE 'Ui, có gì đó sai rồi!';
END;
Ví dụ thực tế: đếm số sinh viên
Giờ mình sẽ ghép tất cả lại thành một ví dụ thực tế. Viết block PL/pgSQL để đếm số sinh viên trong bảng students và in ra thông báo.
DO $$
DECLARE
total_students INT; -- Biến lưu số lượng sinh viên
BEGIN
-- Đếm số sinh viên
SELECT COUNT(*) INTO total_students FROM students;
-- In thông báo kết quả
RAISE NOTICE 'Số lượng sinh viên: %', total_students;
EXCEPTION
-- Xử lý lỗi có thể xảy ra, ví dụ bảng không tồn tại
WHEN OTHERS THEN
RAISE NOTICE 'Đã xảy ra lỗi khi đếm sinh viên.';
END;
$$;
Chạy block này sẽ in ra thông báo trên console. Ví dụ: Số lượng sinh viên: 42.
Lưu ý khi dùng biến
Cùng điểm qua vài điều quan trọng:
Gán giá trị cho biến. Để gán dữ liệu vào biến, bạn dùng lệnh SELECT INTO:
SELECT COUNT(*) INTO total_students FROM students;
Khởi tạo biến. Nếu bạn không gán giá trị cho biến khi khai báo, giá trị mặc định sẽ là NULL.
Ví dụ:
DECLARE
my_var INT; -- Giá trị là NULL
Biến kiểu RECORD. Đây là kiểu biến đa năng, có thể chứa một dòng dữ liệu từ bảng. Ví dụ:
DECLARE
student RECORD;
BEGIN
SELECT * INTO student FROM students WHERE id = 1;
RAISE NOTICE 'Tên sinh viên: %, Tuổi: %', student.name, student.age;
END;
Ví dụ: đếm số khóa học của sinh viên
Giờ giải một bài thực tế: đếm xem một sinh viên đã đăng ký bao nhiêu khóa học và in kết quả.
DO $$
DECLARE
student_id INT := 1; -- ID sinh viên
course_count INT; -- Biến cho số lượng khóa học
BEGIN
-- Đếm số khóa học
SELECT COUNT(*) INTO course_count
FROM enrollments
WHERE student_id = student_id;
-- In thông báo
RAISE NOTICE 'Sinh viên ID % đã đăng ký % khóa học.', student_id, course_count;
EXCEPTION
WHEN OTHERS THEN
RAISE NOTICE 'Đã xảy ra lỗi khi xử lý sinh viên ID %', student_id;
END;
$$;
Block này khá linh hoạt: bạn có thể đổi student_id để kiểm tra số khóa học của các sinh viên khác nhau.
Lỗi và cách tránh
Nếu PL/pgSQL trong bạn đang "quẩy" như hotdog trong lò vi sóng thì cũng bình thường thôi. Lúc mới học dễ gặp mấy lỗi "kinh điển". Đây là vài ví dụ:
Quên khai báo biến. Nếu bạn quên khai báo biến bằng DECLARE, PL/pgSQL sẽ báo lỗi là biến "không tồn tại".
Dùng NULL như giá trị. Nếu biến đã khai báo mà chưa gán giá trị, nó sẽ là NULL. Điều này có thể gây ra hành vi bất ngờ. Ví dụ:
IF my_var = NULL THEN -- SAI!
Dùng IS NULL nhé:
IF my_var IS NULL THEN
Dùng sai phần EXCEPTION. Đôi khi coder bắt tất cả lỗi (WHEN OTHERS) nhưng không ghi gì để xử lý. Như vậy sẽ che giấu lỗi thật. Tốt nhất nên in thông báo lỗi:
RAISE NOTICE 'Lỗi: %', SQLERRM;
GO TO FULL VERSION