Pemicu mungkin ingin Anda inginkan. Namun, membuat ini berfungsi dengan baik dan efisien akan buruk. Mungkin lebih baik untuk tidak menyimpan saldo di setiap baris jika Anda akan terlalu sering memasukkan baris pada tanggal sebelumnya; sebagai gantinya, gunakan kueri atau tampilan untuk menemukan keseimbangan. Untuk menemukan saldo pada tanggal tertentu, gabungkan dengan baris untuk tanggal sebelumnya dan jumlahkan setoran bersih, dengan mengelompokkan berdasarkan ID transaksi saat ini:
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
;
Saya juga membatasi older.pc_id
menjadi kurang dari current.pc_id
untuk memperbaiki ambiguitas yang berkaitan dengan skema dan perhitungan saldo. Sejak pc_date
tidak unik, Anda dapat memiliki beberapa transaksi untuk tanggal tertentu. Jika demikian, berapa seharusnya saldo untuk setiap transaksi? Di sini kita berasumsi bahwa transaksi dengan ID yang lebih besar terjadi setelah transaksi dengan ID yang lebih kecil tetapi memiliki tanggal yang sama. Lebih formal, kami menggunakan pemesanan
Perhatikan bahwa dalam tampilan, kami menggunakan urutan berdasarkan>:
Setelah mencoba membuat pemicu berfungsi dengan baik, saya akan merekomendasikan untuk tidak mencoba. Karena tabel atau baris internal terkunci saat memasukkan/memperbarui, Anda harus memindahkan kolom saldo ke tabel baru, meskipun ini tidak terlalu memberatkan (ganti nama pettycash
ke pettytransactions
, buat pettybalance (balance, pc_id)
tabel, dan buat tampilan bernama pettycash
daripada bergabung dengan pettytransactions
dan pettybalance
di pc_id
). Masalah utamanya adalah badan pemicu dieksekusi sekali untuk setiap baris yang dibuat atau diperbarui, yang akan menyebabkannya menjadi sangat tidak efisien. Alternatifnya adalah dengan membuat prosedur tersimpan
untuk memperbarui kolom, yang dapat Anda panggil setelah memasukkan atau memperbarui. Prosedur lebih berkinerja ketika mendapatkan saldo daripada tampilan, tetapi lebih rapuh karena terserah programmer untuk memperbarui saldo, daripada membiarkan database menanganinya. Menggunakan tampilan adalah desain yang lebih bersih.
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 ;
Di luar topik
Masalah dengan skema saat ini adalah penggunaan Float
s untuk menyimpan nilai moneter. Karena bagaimana angka floating point direpresentasikan, angka yang tepat di basis 10 (yaitu tidak memiliki representasi desimal berulang) tidak selalu tepat sebagai float. Misalnya, 0,01 (dalam basis 10) akan lebih dekat ke 0,09999999776482582... atau 0,0100000000000000002081668... saat disimpan. Ini agak seperti bagaimana 1/3 di basis 3 adalah "0,1" tetapi 0,333333.... di basis 10. Alih-alih Float
, Anda harus menggunakan Decimal
ketik:
ALTER TABLE pettycash MODIFY pc_in DECIMAL(10,2);
ALTER TABLE pettycash MODIFY pc_out DECIMAL(10,2);
Jika menggunakan tampilan, jatuhkan pettycash.pc_bal
. Jika menggunakan prosedur tersimpan untuk memperbarui pettycash.pc_bal
, itu juga harus diubah.