CodeGym /Các khóa học /SQL SELF /Ví dụ về thủ tục phức tạp để xử lý đơn hàng: kiểm tra dữ ...

Ví dụ về thủ tục phức tạp để xử lý đơn hàng: kiểm tra dữ liệu, cập nhật trạng thái, ghi log

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

Hôm nay tụi mình sẽ cùng nhau phân tích cách xây dựng một thủ tục thực tế để xử lý đơn hàng. Nó gồm nhiều bước: kiểm tra dữ liệu, cập nhật trạng thái đơn hàng, và ghi log. Hãy tưởng tượng một nhà hàng, nơi đầu bếp, phục vụ và thu ngân phải phối hợp nhịp nhàng. Trong thủ tục này, tụi mình sẽ làm logic tương tự giữa các bước.

Mô tả bài toán của thủ tục

Thủ tục xử lý đơn hàng cần thực hiện các bước sau:

  1. Kiểm tra xem hàng cần thiết có sẵn trong kho không.
  2. Nếu hàng đủ, trừ số lượng khỏi kho.
  3. Cập nhật trạng thái đơn hàng thành "Đã xử lý".
  4. Ghi thông tin về thao tác thành công vào nhật ký (log).
  5. Nếu có bất kỳ lỗi nào thì rollback về ban đầu.

Triển khai thủ tục

Bước 1. Tạo schema và bảng để làm việc

Trước khi code thủ tục, tụi mình sẽ tạo các bảng mà nó sẽ thao tác.

Bảng orders — đơn hàng

CREATE TABLE orders (
    order_id SERIAL PRIMARY KEY,
    customer_name TEXT NOT NULL,
    product_id INT NOT NULL,
    quantity INT NOT NULL CHECK (quantity > 0),
    status TEXT DEFAULT 'Pending'
);

Bảng này lưu các đơn hàng. Mỗi đơn có khách hàng, id sản phẩm, số lượng và trạng thái (mặc định là "Chờ xử lý").

Bảng inventory — kho hàng

CREATE TABLE inventory (
    product_id SERIAL PRIMARY KEY,
    product_name TEXT NOT NULL UNIQUE,
    stock INT NOT NULL CHECK (stock >= 0)
);

Bảng này là danh sách hàng trong kho. Mỗi sản phẩm có số lượng tồn kho hiện tại (stock).

Bảng order_logs — nhật ký thao tác

CREATE TABLE order_logs (
    log_id SERIAL PRIMARY KEY,
    order_id INT NOT NULL REFERENCES orders(order_id) ON DELETE CASCADE,
    log_message TEXT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Nhật ký này sẽ dùng để ghi thông tin về trạng thái xử lý đơn hàng.

Bước 2. Cấu trúc của thủ tục

Đây là cấu trúc của thủ tục nhiều bước:

  1. Kiểm tra xem hàng yêu cầu có trong kho và đủ số lượng không.
  2. Nếu đủ hàng, giảm số lượng trong bảng inventory.
  3. Đổi trạng thái đơn hàng thành "Đã xử lý".
  4. Ghi kết quả thành công vào bảng order_logs.
  5. Xử lý lỗi có thể xảy ra và rollback nếu cần.

Bước 3. Viết thủ tục

Giờ tụi mình sẽ viết thủ tục process_order để thực hiện các bước trên.

CREATE OR REPLACE FUNCTION process_order(order_id INT)
RETURNS VOID AS $$
DECLARE
    v_product_id INT;
    v_quantity INT;
    v_stock INT;
BEGIN
    -- Bước 1: Lấy thông tin đơn hàng
    SELECT product_id, quantity
    INTO v_product_id, v_quantity
    FROM orders
    WHERE order_id = $1;

    -- Kiểm tra đơn hàng có tồn tại không
    IF NOT FOUND THEN
        RAISE EXCEPTION 'Đơn hàng với ID % không tồn tại.', $1;
    END IF;

    -- Bước 2: Kiểm tra hàng trong kho
    SELECT stock INTO v_stock
    FROM inventory
    WHERE product_id = v_product_id;

    IF NOT FOUND THEN
        RAISE EXCEPTION 'Sản phẩm với ID % không có trong kho.', v_product_id;
    END IF;

    IF v_stock < v_quantity THEN
        RAISE EXCEPTION 'Không đủ hàng cho sản phẩm ID %. Yêu cầu: %, Có sẵn: %.',
            v_product_id, v_quantity, v_stock;
    END IF;

    -- Bước 3: Giảm số lượng hàng trong kho
    UPDATE inventory
    SET stock = stock - v_quantity
    WHERE product_id = v_product_id;

    -- Bước 4: Cập nhật trạng thái đơn hàng thành 'Processed'
    UPDATE orders
    SET status = 'Processed'
    WHERE order_id = $1;

    -- Bước 5: Ghi log thành công
    INSERT INTO order_logs (order_id, log_message)
    VALUES ($1, 'Đơn hàng đã xử lý thành công.');

EXCEPTION
    WHEN OTHERS THEN
        -- Ghi log lỗi nếu có bất kỳ thất bại nào
        INSERT INTO order_logs (order_id, log_message)
        VALUES ($1, 'Lỗi khi xử lý đơn hàng: ' || SQLERRM);

        -- Rollback tất cả thay đổi
        RAISE;
END;
$$ LANGUAGE plpgsql;

Cùng phân tích thủ tục này nhé.

  1. Bước kiểm tra:

    tụi mình kiểm tra xem đơn hàng có tồn tại trong bảng orders không. Nếu không tìm thấy, sẽ raise exception với thông báo chi tiết. Tương tự, kiểm tra hàng trong kho và số lượng.

  2. Bước thao tác với kho:

    nếu đủ hàng, giảm số lượng trong kho bằng lệnh UPDATE.

  3. Bước đổi trạng thái đơn hàng:

    đổi trạng thái thành "Processed" (Đã xử lý) để đánh dấu đơn đã hoàn thành.

  4. Bước ghi log:

    sau khi xử lý thành công, thêm thông báo vào bảng order_logs để lưu lại thao tác.

  5. Xử lý exception:

    nếu có lỗi, tụi mình bắt lỗi trong block EXCEPTION, ghi log chi tiết về lỗi và rollback mọi thay đổi.

Ví dụ sử dụng

Tạo dữ liệu test để kiểm tra thủ tục của tụi mình.

-- Thêm hàng vào kho
INSERT INTO inventory (product_name, stock)
VALUES ('Laptop', 10), ('Monitor', 5);

-- Thêm đơn hàng
INSERT INTO orders (customer_name, product_id, quantity)
VALUES
    ('Alice', 1, 2),
    ('Bob', 2, 1),
    ('Charlie', 1, 20); -- Đơn này sẽ gây lỗi

Giờ test thủ tục nhé:

-- Xử lý đơn của Alice
SELECT process_order(1);

-- Xử lý đơn của Bob
SELECT process_order(2);

-- Thử xử lý đơn của Charlie (lỗi)
SELECT process_order(3);

Kết quả:

  • Đơn của Alice và Bob sẽ được xử lý thành công, ghi log và số lượng kho giảm đi.
  • Đơn của Charlie sẽ gây lỗi vì không đủ hàng trong kho, và log lỗi sẽ được ghi lại.

Kiểm tra các bảng sau khi chạy lệnh:

SELECT * FROM inventory; -- Thay đổi số lượng kho
SELECT * FROM orders; -- Thay đổi trạng thái đơn hàng
SELECT * FROM order_logs; -- Ghi log thao tác

Lỗi thường gặp và mẹo

  1. Lỗi: quên kiểm tra NOT FOUND sau SELECT INTO.

    Luôn xử lý trường hợp truy vấn trả về rỗng, nếu không sẽ bị exception bất ngờ.

  2. Lỗi: không thêm block EXCEPTION.

    Nếu không có xử lý lỗi trong thủ tục, khi exception xảy ra transaction có thể bị treo hoặc sai logic.

  3. Mẹo: bảo vệ khỏi SQL injection.

    Dùng tham số kiểu dữ liệu chặt chẽ và tránh SQL động nếu không cần thiết.

Mở rộng thủ tục

Trong thực tế, bạn có thể thêm nhiều kiểm tra hơn, ví dụ:

  • Tính khuyến mãi hoặc giảm giá cho khách hàng.
  • Kiểm tra hạn mức tín dụng của khách trước khi xử lý đơn.
  • Ghi log cả khi rollback chứ không chỉ khi thành công.
2
Nhiệm vụ
SQL SELF, mức độ, bài học
Đã khóa
Thủ tục xử lý đơn hàng
Thủ tục xử lý đơn hàng
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION