CodeGym /Các khóa học /SQL SELF /Các toán tử RAISE NOTICE, RETURN QUERY

Các toán tử RAISE NOTICE, RETURN QUERY

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

Trong lập trình có hai thứ quan trọng: hiểu chuyện gì đang xảy ra (nhất là khi mọi thứ không như ý bạn), và trả về dữ liệu hữu ích. Điều này càng đúng với PL/pgSQL, vì mọi thứ chạy phía server, nên debug không phải lúc nào cũng dễ. May mà có mấy công cụ tích hợp sẵn:

RAISE NOTICE — đây là cách để in thông báo khi function chạy. Cứ coi nó như console.log trong JavaScript hoặc print trong Python ấy. Bạn có thể show giá trị biến, trạng thái hiện tại hoặc đơn giản là để lại "lời nhắn" cho chính mình sau này.

RETURN QUERY — đây là cách trả về một tập dữ liệu, ví dụ cả một bảng hoặc kết quả query phức tạp. Nhờ nó, function PL/pgSQL giống như một truy vấn SQL thực thụ luôn.

Lệnh RAISE NOTICE

RAISE NOTICE cho phép in thông báo ra màn hình khi function chạy. Cú pháp như sau:

RAISE NOTICE 'Thông báo: %', giá_trị;
  • % — là placeholder cho biến, giống như printf trong C.
  • Sau phần text bạn có thể liệt kê các biến cần chèn vào.

Ví dụ sử dụng

Giả sử bạn viết function đếm số sinh viên trong các nhóm khác nhau. Bạn muốn xem giá trị trung gian để biết mọi thứ có ổn không.

CREATE OR REPLACE FUNCTION count_students_in_groups() RETURNS VOID AS $$
DECLARE
    group_name TEXT;
    student_count INT;
BEGIN
    FOR group_name IN SELECT DISTINCT group_name FROM students LOOP
        SELECT COUNT(*) INTO student_count
        FROM students WHERE group_name = group_name;

        -- In kết quả
        RAISE NOTICE 'Nhóm: %, Số lượng sinh viên: %', group_name, student_count;
    END LOOP;
END;
$$ LANGUAGE plpgsql;

Khi bạn gọi function này:

SELECT count_students_in_groups();

Bạn sẽ thấy các thông báo kiểu như sau trong log:

NOTICE:  Nhóm: Toán học, Số lượng sinh viên: 30
NOTICE:  Nhóm: Triết học, Số lượng sinh viên: 25
NOTICE:  Nhóm: Sinh học, Số lượng sinh viên: 18

Lưu ý là function này không trả về gì (tạo với RETURNS VOID), nhưng RAISE NOTICE cho bạn thấy vòng lặp đang chạy thế nào.

Mẹo hay với RAISE

Ngoài NOTICE, bạn còn có thể dùng các mức thông báo khác:

  • RAISE DEBUG — cho thông tin chi tiết hơn (chỉ hiện nếu bật log mức DEBUG).
  • RAISE INFO — cho thông tin chung.
  • RAISE WARNING — cho cảnh báo.
  • RAISE EXCEPTION — để tạo lỗi, phần này sẽ học sau.

Debug thì nên dùng NOTICE hoặc DEBUG, vì chúng tiện và không làm dừng function.

Lệnh RETURN QUERY: trả dữ liệu như dân chuyên

RETURN QUERY dùng trong PL/pgSQL để trả về tập các dòng. Nhờ nó, bạn có thể trả kết quả truy vấn SQL trực tiếp từ function. Cú pháp như sau:

RETURN QUERY <truy vấn SQL>;

Cũng có thể kết hợp nhiều truy vấn:

RETURN QUERY <truy vấn SQL 1>;
RETURN QUERY <truy vấn SQL 2>;

Ví dụ 1: function với RETURN QUERY

Viết function trả về danh sách sinh viên trong một nhóm nhất định.

CREATE OR REPLACE FUNCTION get_students_by_group(group_name TEXT)
RETURNS TABLE(id INT, name TEXT) AS $$
BEGIN
    RETURN QUERY
    SELECT id, name 
    FROM students
    WHERE group_name = group_name;
END;
$$ LANGUAGE plpgsql;

Giờ gọi function này:

SELECT * FROM get_students_by_group('Toán học');

Để test function, tạo bảng students và thêm dữ liệu vào trước đã:

CREATE TABLE students (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
group_name TEXT NOT NULL
);

INSERT INTO students (name, group_name) VALUES
('Otto Song',     'Vật lý'),
('Alex Lin',      'Toán học'),
('Anna Vel',      'Toán học'),
('Maria Chi',     'Lịch sử');

Kết quả:

id name
2 Alex Lin
3 Anna Vel

Nhìn xem, function chạy như một truy vấn SQL bình thường luôn.

Ví dụ 2: Kết hợp nhiều truy vấn

Nếu bạn muốn trả về dữ liệu kết hợp từ nhiều bảng thì sao? Hãy trả về danh sách sinh viên và các khóa học mà họ đã đăng ký.

CREATE OR REPLACE FUNCTION get_students_and_courses()
RETURNS TABLE(student_name TEXT, course_name TEXT) AS $$
BEGIN
    RETURN QUERY
    SELECT s.name, c.name
    FROM students s
    JOIN enrollments e ON s.id = e.student_id
    JOIN courses c ON e.course_id = c.id;
END;
$$ LANGUAGE plpgsql;

Đầu tiên tạo ba bảng: students, courses và enrollments, rồi thêm dữ liệu:

-- Bảng sinh viên
CREATE TABLE students (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL
);

-- Bảng khóa học
CREATE TABLE courses (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL
);

-- Bảng đăng ký khóa học (liên kết)
CREATE TABLE enrollments (
student_id INT REFERENCES students(id),
course_id INT REFERENCES courses(id)
);

-- Thêm sinh viên
INSERT INTO students (name) VALUES
('Otto Song'),
('Alex Lin'),
('Anna Vel'),
('Maria Chi');

-- Thêm khóa học
INSERT INTO courses (name) VALUES
('Toán học'),
('Vật lý'),
('Lịch sử');

-- Thêm đăng ký khóa học
INSERT INTO enrollments (student_id, course_id) VALUES
(1, 2), -- Otto -> Vật lý
(2, 1), -- Alex -> Toán học
(3, 1), -- Anna -> Toán học
(4, 3); -- Maria -> Lịch sử

Gọi function này:

SELECT * FROM get_students_and_courses();

sẽ cho kết quả như sau:

student_name course_name
Otto Song Vật lý
Alex Lin Toán học
Anna Vel Toán học
Maria Chi Lịch sử

Function này gộp dữ liệu từ ba bảng và cho bạn biết sinh viên nào đăng ký khóa nào.

Kết hợp RAISE NOTICERETURN QUERY

Đôi khi RETURN QUERYRAISE NOTICE cùng xuất hiện trong một function, để bạn vừa kiểm soát quá trình chạy vừa xem được kết quả trung gian.

Ví dụ function dưới đây trả về dữ liệu sinh viên và đồng thời in thông báo để bạn thấy tiến trình:

CREATE OR REPLACE FUNCTION debug_students()
RETURNS TABLE(student_id INT, student_name TEXT) AS $$
DECLARE
    count_students INT;
BEGIN
    -- Đếm số sinh viên
    SELECT COUNT(*) INTO count_students FROM students;
    RAISE NOTICE 'Tổng số sinh viên: %', count_students;

    -- Trả về dữ liệu sinh viên
    RETURN QUERY
    SELECT id, name FROM students;

    RAISE NOTICE 'Function đã chạy xong.';
END;
$$ LANGUAGE plpgsql;

Nếu bảng students chưa có, hãy tạo và thêm dữ liệu:

CREATE TABLE students (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL
);

INSERT INTO students (name) VALUES
('Otto Song'),
('Alex Lin'),
('Anna Vel'),
('Maria Chi');

Giờ gọi function này:

SELECT * FROM debug_students();

Bạn sẽ nhận được cả dữ liệu lẫn thông báo:

student_id student_name
1 Otto Song
2 Alex Lin
3 Anna Vel
4 Maria Chi

Thông báo in ra console:

NOTICE:  Tổng số sinh viên: 4
NOTICE:  Function đã chạy xong.

Lỗi thường gặp khi sử dụng

Lỗi với biến trong RAISE NOTICE: nếu bạn quên khai báo biến hoặc viết sai tên biến, sẽ bị lỗi variable does not exist. Luôn kiểm tra biến đã khai báo đúng chưa nhé.

Lỗi kiểu trả về: nếu bạn dùng RETURN QUERY mà không khai báo RETURNS TABLE khi tạo function, PostgreSQL sẽ báo lỗi. Đảm bảo kiểu trả về khớp với dữ liệu trả về.

Lỗi với placeholder trong RAISE: nếu số lượng placeholder % không khớp với số biến, sẽ bị lỗi. Ví dụ:

RAISE NOTICE 'Giá trị: %, %', value1;

Đoạn này sẽ lỗi vì thiếu biến thứ hai.

Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION