CodeGym /Các khóa học /SQL SELF /Tương tác giữa function và procedure

Tương tác giữa function và procedure

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

Ở mấy level trước mình đã nói sơ về procedure và function trong PostgreSQL rồi. Giờ là lúc đào sâu hơn chút nữa.

Function và procedure có thể chạy độc lập, nhưng thường thì sự kết hợp của tụi nó mới quyết định thành công của cả hệ thống. Điểm tiện nhất là function có thể gọi function khác, truyền dữ liệu và thậm chí lấy luôn kết quả trả về.

Function vs Procedure: khác nhau chỗ nào?

Nhớ lại xem function khác procedure ở PostgreSQL như nào nhé:

  • Function (FUNCTION):

    • Trả về giá trị.
    • Có thể dùng trong SELECT.
    • Thường dùng để tính toán hoặc chuyển đổi dữ liệu.
  • Procedure (PROCEDURE):

    • Không trả về giá trị trực tiếp.
    • Dùng để thực hiện thao tác như insert, update hoặc xóa dữ liệu.
    • Gọi bằng lệnh CALL.

Truyền dữ liệu giữa các function

Vào phần thực hành luôn, bắt đầu với ví dụ cơ bản về truyền dữ liệu giữa function và procedure. Thực chất, truyền dữ liệu giữa các function là qua tham số và giá trị trả về.

Đây là ví dụ gọi function trong function khác:

CREATE OR REPLACE FUNCTION get_student_name(student_id INT)
RETURNS TEXT AS $$
DECLARE
    student_name TEXT;
BEGIN
    -- Lấy tên sinh viên theo ID
    SELECT name INTO student_name FROM students WHERE id = student_id;

    -- Trả về tên
    RETURN student_name;
END;
$$ LANGUAGE plpgsql;

Function này có thể được gọi từ function khác:

CREATE OR REPLACE FUNCTION welcome_student(student_id INT)
RETURNS TEXT AS $$
DECLARE
    message TEXT;
BEGIN
    -- Lấy tên sinh viên bằng function khác
    message := 'Chào mừng, ' || get_student_name(student_id) || '!';

    -- Trả về lời chào
    RETURN message;
END;
$$ LANGUAGE plpgsql;
  1. Function get_student_name trả về tên sinh viên theo id (student_id).
  2. Ở function khác — welcome_student — tên này được dùng để tạo message chào mừng.

Lưu ý: Lấy dữ liệu bằng SELECT INTO sẽ lưu kết quả truy vấn vào biến PL/pgSQL.

Ví dụ gọi procedure từ function

Giờ thử xem gọi procedure từ function như nào nhé. Giả sử có procedure ghi lại thời gian sinh viên đăng nhập hệ thống:

CREATE OR REPLACE PROCEDURE log_student_entry(student_id INT)
LANGUAGE plpgsql
AS $$
BEGIN
    INSERT INTO log_entries(student_id, entry_time)
    VALUES (student_id, NOW());
END;
$$;

Giờ gọi procedure này từ function, để vừa log vừa trả về message:

CREATE OR REPLACE FUNCTION student_login(student_id INT)
RETURNS TEXT AS $$
BEGIN
    -- Gọi procedure để log
    CALL log_student_entry(student_id);

    -- Trả về message
    RETURN 'Đăng nhập sinh viên đã được ghi lại thành công.';
END;
$$ LANGUAGE plpgsql;

Ví dụ thực tế về tương tác

Ví dụ 1: tính tổng đơn hàng và log lại

Giả sử bạn làm hệ thống đặt hàng online. Để tính tổng đơn hàng, bạn có function:

CREATE OR REPLACE FUNCTION calculate_order_total(order_id INT)
RETURNS NUMERIC AS $$
DECLARE
    total NUMERIC;
BEGIN
    -- Tính tổng tất cả các mục trong đơn
    SELECT SUM(price * quantity) INTO total
    FROM order_items
    WHERE order_id = order_id;

    RETURN total;
END;
$$ LANGUAGE plpgsql;

Để lưu tổng đơn hàng, dùng procedure:

CREATE OR REPLACE PROCEDURE log_order_total(order_id INT, total NUMERIC)
LANGUAGE plpgsql
AS $$
BEGIN
    INSERT INTO order_totals(order_id, total)
    VALUES (order_id, total);
END;
$$;

Giờ kết hợp lại:

CREATE OR REPLACE FUNCTION process_order(order_id INT)
RETURNS TEXT AS $$
DECLARE
    total NUMERIC;
BEGIN
    -- Gọi function tính tổng
    total := calculate_order_total(order_id);

    -- Log tổng qua procedure
    CALL log_order_total(order_id, total);

    RETURN 'Đơn hàng đã xử lý thành công.';
END;
$$ LANGUAGE plpgsql;

Ví dụ 2: lấy rating cao nhất của sinh viên và cập nhật profile

Function lấy rating cao nhất:

CREATE OR REPLACE FUNCTION get_highest_rating(student_id INT)
RETURNS INT AS $$
DECLARE
    max_rating INT;
BEGIN
    -- Tìm rating cao nhất của sinh viên
    SELECT MAX(rating) INTO max_rating
    FROM ratings
    WHERE student_id = student_id;

    RETURN max_rating;
END;
$$ LANGUAGE plpgsql;

Procedure cập nhật profile sinh viên:

CREATE OR REPLACE PROCEDURE update_student_profile(student_id INT, max_rating INT)
LANGUAGE plpgsql
AS $$
BEGIN
    UPDATE students
    SET highest_rating = max_rating
    WHERE id = student_id;
END;
$$;

Function gọi hai thao tác trên:

CREATE OR REPLACE FUNCTION refresh_student_profile(student_id INT)
RETURNS TEXT AS $$
DECLARE
    max_rating INT;
BEGIN
    -- Lấy rating cao nhất
    max_rating := get_highest_rating(student_id);

    -- Cập nhật profile sinh viên
    CALL update_student_profile(student_id, max_rating);

    RETURN 'Profile đã được cập nhật thành công.';
END;
$$ LANGUAGE plpgsql;

Lỗi thường gặp khi tương tác

Một lỗi phổ biến là không khớp kiểu dữ liệu giữa function và procedure. Ví dụ, procedure cần tham số kiểu NUMERIC mà bạn lại truyền INTEGER, PostgreSQL sẽ báo lỗi không khớp kiểu. Luôn kiểm tra kiểu dữ liệu cho khớp nhé.

Lỗi nữa là gọi vòng lặp function, kiểu function A gọi function B, rồi B lại gọi A. Như vậy sẽ bị lặp vô tận và sập hệ thống luôn.

Ý nghĩa thực tế

Tại sao phải tương tác như vậy? Ngoài đời, function và procedure giống như "block xây dựng" của hệ thống phức tạp. Chia nhỏ code ra giúp dễ debug, tái sử dụng và test hơn. Ví dụ:

  • Phỏng vấn có thể yêu cầu bạn viết function gọi procedure để xử lý thao tác phức tạp. Thể hiện được kỹ năng này là điểm cộng lớn.
  • Khi làm app thực tế như shop online, hệ thống log hay CRM, biết tổ chức code bằng function và procedure sẽ giúp code gọn và dễ quản lý hơn nhiều.

Muốn tìm hiểu sâu hơn về tương tác giữa function và procedure thì đọc thêm tài liệu chính thức về PL/pgSQL nhé.

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