CodeGym /Các khóa học /SQL SELF /Cơ bản về cú pháp PL/pgSQL

Cơ bản về cú pháp PL/pgSQL

SQL SELF
Mức độ , Bài học
Có sẵn

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:

  1. DECLARE: khai báo biến (có thể có hoặc không).
  2. BEGIN ... END: block thực thi chính, nơi logic được thực hiện.
  3. 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é.

  1. 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.

  1. 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;
  1. 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;
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION