Trình kích hoạt có lẽ bạn muốn bạn muốn. Tuy nhiên, làm cho điều này hoạt động đúng cách và hiệu quả sẽ rất xấu. Có lẽ tốt hơn là không nên lưu trữ số dư trong mỗi hàng nếu bạn thường xuyên chèn các hàng vào những ngày trước đó; thay vào đó, hãy sử dụng truy vấn hoặc chế độ xem để tìm số dư. Để tìm số dư vào một ngày cụ thể, hãy nối số dư đó với các hàng cho các ngày trước đó và tính tổng số tiền gửi ròng, nhóm theo ID giao dịch hiện tại:
CREATE VIEW pettybalance
AS SELECT SUM(older.pc_in - older.pc_out) AS balance,
current.pc_id AS pc_id, -- foreign key
current.pc_date AS `date`
FROM pettycash AS current
JOIN pettycash AS older
ON current.pc_date > older.pc_date
OR (current.pc_date = older.pc_date AND current.pc_id >= older.pc_id)
GROUP BY current.pc_id
;
Tôi cũng hạn chế older.pc_id
nhỏ hơn current.pc_id
để khắc phục sự không rõ ràng liên quan đến lược đồ và phép tính số dư. Kể từ pc_date
không phải là duy nhất, bạn có thể có nhiều giao dịch trong một ngày nhất định. Nếu đúng như vậy, số dư cho mỗi giao dịch phải là bao nhiêu? Ở đây, chúng tôi giả định rằng một giao dịch có ID lớn hơn xảy ra sau một giao dịch có ID nhỏ hơn nhưng có cùng ngày. Chính thức hơn, chúng tôi sử dụng đặt hàng
Lưu ý rằng trong chế độ xem, chúng tôi sử dụng thứ tự ≥ dựa trên>:
Sau khi cố gắng làm cho các trình kích hoạt hoạt động bình thường, tôi khuyên bạn không nên thử. Do bảng nội bộ hoặc khóa hàng khi chèn / cập nhật, bạn phải di chuyển cột số dư sang bảng mới, mặc dù điều này không quá phức tạp (đổi tên pettycash
tới pettytransactions
, tạo một pettybalance (balance, pc_id)
bảng và tạo một dạng xem có tên pettycash
hơn tham gia pettytransactions
và pettybalance
trên pc_id
). Vấn đề chính là các cơ quan trình kích hoạt thực thi một lần cho mỗi hàng được tạo hoặc cập nhật, điều này sẽ khiến chúng cực kỳ kém hiệu quả. Một giải pháp thay thế là tạo thủ tục được lưu trữ
để cập nhật các cột mà bạn có thể gọi sau khi chèn hoặc cập nhật. Một thủ tục có hiệu suất cao hơn khi lấy số dư hơn là một khung nhìn, nhưng dễ hỏng hơn vì các lập trình viên phải cập nhật số dư, thay vì để cơ sở dữ liệu xử lý nó. Sử dụng chế độ xem là thiết kế gọn gàng hơn.
DROP PROCEDURE IF EXISTS update_balance;
delimiter ;;
CREATE PROCEDURE update_balance (since DATETIME)
BEGIN
DECLARE sincebal DECIMAL(10,2);
SET sincebal = (
SELECT pc_bal
FROM pettycash AS pc
WHERE pc.pc_date < since
ORDER BY pc.pc_date DESC, pc.pc_id DESC LIMIT 1
);
IF ISNULL(sincebal) THEN
SET sincebal=0.0;
END IF;
UPDATE pettycash AS pc
SET pc_bal=(
SELECT sincebal+SUM(net)
FROM (
SELECT pc_id, pc_in - pc_out AS net, pc_date
FROM pettycash
WHERE since <= pc_date
) AS older
WHERE pc.pc_date > older.pc_date
OR (pc.pc_date = older.pc_date
AND pc.pc_id >= older.pc_id)
) WHERE pc.pc_date >= since;
END;;
delimiter ;
Lạc đề
Một vấn đề với lược đồ hiện tại là việc sử dụng Float
s để lưu trữ các giá trị tiền tệ. Do cách biểu diễn số dấu phẩy động, các số chính xác trong cơ số 10 (tức là không có biểu diễn thập phân lặp lại) không phải lúc nào cũng chính xác như số thực. Ví dụ:0,01 (trong cơ số 10) sẽ gần hơn với 0,009999999776482582 ... hoặc 0,0100000000000000002081668 ... khi được lưu trữ. Nó giống như cách 1/3 trong cơ sở 3 là "0,1" nhưng 0,333333 .... trong cơ sở 10. Thay vì Float
, bạn nên sử dụng Decimal
loại:
ALTER TABLE pettycash MODIFY pc_in DECIMAL(10,2);
ALTER TABLE pettycash MODIFY pc_out DECIMAL(10,2);
Nếu sử dụng một chế độ xem, hãy thả pettycash.pc_bal
. Nếu sử dụng quy trình được lưu trữ để cập nhật pettycash.pc_bal
, nó cũng nên được thay đổi.