1. Apakah pemicunya mengikuti prinsip ACID database relasional? Apakah ada kemungkinan penyisipan dilakukan tetapi pemicu gagal?
Pertanyaan ini sebagian dijawab dalam pertanyaan terkait yang Anda tautkan. Kode pemicu dijalankan dalam konteks transaksional yang sama dengan pernyataan DML yang menyebabkannya menyala, mempertahankan bagian Atom dari prinsip-prinsip ACID yang Anda sebutkan. Pernyataan pemicu dan kode pemicu berhasil atau gagal sebagai satu unit.
Properti ACID juga menjamin seluruh transaksi (termasuk kode pemicu) akan membuat database dalam keadaan yang tidak melanggar kendala eksplisit ( Konsisten ) dan setiap efek komitmen yang dapat dipulihkan akan selamat dari kerusakan database ( Tahan Lama ).
Kecuali transaksi di sekitarnya (mungkin implisit atau komit otomatis) berjalan pada SERIALIZABLE
tingkat isolasi , properti Terisolasi tidak dijamin secara otomatis. Aktivitas database bersamaan lainnya dapat mengganggu operasi kode pemicu Anda dengan benar. Misalnya, saldo akun dapat diubah dengan sesi lain setelah Anda membacanya dan sebelum Anda memperbaruinya - kondisi balapan klasik.
2. Pernyataan IF dan UPDATE saya terlihat aneh. Apakah ada cara yang lebih baik untuk memperbarui baris [Akun] yang benar?
Ada beberapa alasan bagus mengapa pertanyaan lain yang Anda tautkan tidak menawarkan solusi berbasis pemicu. Kode pemicu yang dirancang untuk menjaga agar struktur denormalized tersinkronisasi dapat sangat sulit untuk dilakukan dengan benar dan diuji dengan benar. Bahkan orang-orang SQL Server yang sangat canggih dengan pengalaman bertahun-tahun berjuang dengan ini.
Mempertahankan kinerja yang baik sekaligus menjaga kebenaran di semua skenario dan menghindari masalah seperti deadlock menambah dimensi kesulitan tambahan. Kode pemicu Anda sangat kuat, dan memperbarui saldo setiap akun meskipun hanya satu transaksi yang dimodifikasi. Ada segala macam risiko dan tantangan dengan solusi berbasis pemicu, yang membuat tugas ini sangat tidak cocok untuk seseorang yang relatif baru di bidang teknologi ini.
Untuk menggambarkan beberapa masalah, saya menunjukkan beberapa kode contoh di bawah ini. Ini bukan solusi yang telah teruji ketat (pemicunya sulit!) Dan saya tidak menyarankan Anda menggunakannya sebagai apa pun selain latihan pembelajaran. Untuk sistem nyata, solusi non-pemicu memiliki manfaat penting, jadi Anda harus hati-hati meninjau jawaban untuk pertanyaan lain , dan menghindari ide pemicu sepenuhnya.
Tabel sampel
CREATE TABLE dbo.Accounts
(
AccountID integer NOT NULL,
Balance money NOT NULL,
CONSTRAINT PK_Accounts_ID
PRIMARY KEY CLUSTERED (AccountID)
);
CREATE TABLE dbo.Transactions
(
TransactionID integer IDENTITY NOT NULL,
AccountID integer NOT NULL,
Amount money NOT NULL,
CONSTRAINT PK_Transactions_ID
PRIMARY KEY CLUSTERED (TransactionID),
CONSTRAINT FK_Accounts
FOREIGN KEY (AccountID)
REFERENCES dbo.Accounts (AccountID)
);
Mencegah TRUNCATE TABLE
Pemicu tidak dipecat oleh TRUNCATE TABLE
. Tabel kosong berikut ada murni untuk mencegah Transactions
tabel terpotong (dirujuk oleh kunci asing mencegah pemotongan tabel):
CREATE TABLE dbo.PreventTransactionsTruncation
(
Dummy integer NULL,
CONSTRAINT FK_Transactions
FOREIGN KEY (Dummy)
REFERENCES dbo.Transactions (TransactionID),
CONSTRAINT CHK_NoRows
CHECK (Dummy IS NULL AND Dummy IS NOT NULL)
);
Definisi Pemicu
Kode pemicu berikut memastikan hanya entri akun yang perlu dipelihara, dan menggunakan SERIALIZABLE
semantik di sana. Sebagai efek samping yang diinginkan, ini juga menghindari hasil yang salah yang mungkin terjadi jika tingkat isolasi versi baris digunakan. Kode juga menghindari mengeksekusi kode pemicu jika tidak ada baris yang dipengaruhi oleh pernyataan sumber. Tabel dan RECOMPILE
petunjuk sementara digunakan untuk menghindari pemicu masalah rencana pelaksanaan yang disebabkan oleh perkiraan kardinalitas yang tidak akurat:
CREATE TRIGGER dbo.TransactionChange ON dbo.Transactions
AFTER INSERT, UPDATE, DELETE
AS
BEGIN
IF @@ROWCOUNT = 0 OR
TRIGGER_NESTLEVEL
(
OBJECT_ID(N'dbo.TransactionChange', N'TR'),
'AFTER',
'DML'
) > 1
RETURN;
SET NOCOUNT, XACT_ABORT ON;
CREATE TABLE #Delta
(
AccountID integer PRIMARY KEY,
Amount money NOT NULL
);
INSERT #Delta
(AccountID, Amount)
SELECT
InsDel.AccountID,
Amount = SUM(InsDel.Amount)
FROM
(
SELECT AccountID, Amount
FROM Inserted
UNION ALL
SELECT AccountID, $0 - Amount
FROM Deleted
) AS InsDel
GROUP BY
InsDel.AccountID;
UPDATE A
SET Balance += D.Amount
FROM #Delta AS D
JOIN dbo.Accounts AS A WITH (SERIALIZABLE)
ON A.AccountID = D.AccountID
OPTION (RECOMPILE);
END;
Pengujian
Kode berikut menggunakan tabel angka untuk membuat 100.000 akun dengan saldo nol:
INSERT dbo.Accounts
(AccountID, Balance)
SELECT
N.n, $0
FROM dbo.Numbers AS N
WHERE
N.n BETWEEN 1 AND 100000;
Kode tes di bawah ini menyisipkan 10.000 transaksi acak:
INSERT dbo.Transactions
(AccountID, Amount)
SELECT
CONVERT(integer, RAND(CHECKSUM(NEWID())) * 100000 + 1),
CONVERT(money, RAND(CHECKSUM(NEWID())) * 500 - 250)
FROM dbo.Numbers AS N
WHERE
N.n BETWEEN 1 AND 10000;
Menggunakan alat SQLQueryStress , saya menjalankan tes ini 100 kali pada 32 utas dengan kinerja yang baik, tanpa deadlock, dan hasil yang benar. Saya masih tidak merekomendasikan ini sebagai apa pun selain latihan belajar.