Saya berasumsi bahwa database selalu memeriksa nilai default, bahkan jika saya memberikan nilai yang benar, maka saya melakukan pekerjaan yang sama dua kali.
Um, mengapa Anda menganggap itu? ;-). Mengingat bahwa Default ada untuk memberikan nilai ketika kolom yang dilampirkan tidak ada dalam INSERT
pernyataan, saya akan mengasumsikan sebaliknya: bahwa mereka sepenuhnya diabaikan jika kolom terkait hadir diINSERT
pernyataan.
Untungnya, tidak satu pun dari kita perlu berasumsi apa pun karena pernyataan ini dalam pertanyaan:
Saya sebagian besar tertarik pada kinerja.
Pertanyaan tentang kinerja hampir selalu dapat diuji. Jadi kita hanya perlu membuat tes untuk mengizinkan SQL Server (otoritas sebenarnya di sini) untuk menjawab pertanyaan ini.
MEMPERSIAPKAN
Jalankan yang berikut ini sekali:
SET NOCOUNT ON;
-- DROP TABLE #HasDefault;
CREATE TABLE #HasDefault
(
[HasDefaultID] INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
[SomeInt] INT NULL,
[SomeDate] DATETIME NOT NULL DEFAULT (GETDATE())
);
-- DROP TABLE #NoDefault;
CREATE TABLE #NoDefault
(
[NoDefaultID] INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
[SomeInt] INT NULL,
[SomeDate] DATETIME NOT NULL
);
-- make sure that data file and Tran Log file are grown, if need be, ahead of time:
INSERT INTO #HasDefault ([SomeInt])
SELECT TOP (2000000) NULL
FROM [master].sys.[all_columns] ac1
CROSS JOIN [master].sys.[all_columns] ac2;
Jalankan tes 1A dan 1B secara individual, tidak bersamaan karena itu mengacaukan waktunya. Jalankan masing-masing beberapa kali untuk mendapatkan rasa waktu rata-rata untuk masing-masing.
Tes 1A
TRUNCATE TABLE #HasDefault;
GO
PRINT '#HasDefault:';
SET STATISTICS TIME ON;
INSERT INTO #HasDefault ([SomeDate])
SELECT TOP (1000000) '2017-05-15 10:11:12.000'
FROM [master].sys.[all_columns] ac1
CROSS JOIN [master].sys.[all_columns] ac2;
SET STATISTICS TIME OFF;
GO
Tes 1B
TRUNCATE TABLE #NoDefault;
GO
PRINT '#NoDefault:';
SET STATISTICS TIME ON;
INSERT INTO #NoDefault ([SomeDate])
SELECT TOP (1000000) '2017-05-15 10:11:12.000'
FROM [master].sys.[all_columns] ac1
CROSS JOIN [master].sys.[all_columns] ac2;
SET STATISTICS TIME OFF;
GO
Jalankan tes 2A dan 2B secara individual, tidak bersamaan karena itu mengacaukan waktunya. Jalankan masing-masing beberapa kali untuk mendapatkan rasa waktu rata-rata untuk masing-masing.
Tes 2A
TRUNCATE TABLE #HasDefault;
GO
DECLARE @Counter INT = 0,
@StartTime DATETIME,
@EndTime DATETIME;
BEGIN TRAN;
--SET STATISTICS TIME ON;
SET @StartTime = GETDATE();
WHILE (@Counter < 100000)
BEGIN
INSERT INTO #HasDefault ([SomeDate]) VALUES ('2017-05-15 10:11:12.000');
SET @Counter = @Counter + 1;
END;
SET @EndTime = GETDATE();
--SET STATISTICS TIME OFF;
COMMIT TRAN;
PRINT DATEDIFF(MILLISECOND, @StartTime, @EndTime);
Tes 2B
TRUNCATE TABLE #NoDefault;
GO
DECLARE @Counter INT = 0,
@StartTime DATETIME,
@EndTime DATETIME;
BEGIN TRAN;
--SET STATISTICS TIME ON;
SET @StartTime = GETDATE();
WHILE (@Counter < 100000)
BEGIN
INSERT INTO #NoDefault ([SomeDate]) VALUES ('2017-05-15 10:11:12.000');
SET @Counter = @Counter + 1;
END;
SET @EndTime = GETDATE();
--SET STATISTICS TIME OFF;
COMMIT TRAN;
PRINT DATEDIFF(MILLISECOND, @StartTime, @EndTime);
Anda harus melihat bahwa tidak ada perbedaan nyata dalam pengaturan waktu antara tes 1A dan 1B, atau antara tes 2A dan 2B. Jadi, tidak, tidak ada penalti kinerja yang telah DEFAULT
ditentukan tetapi tidak digunakan.
Selain itu, selain hanya mendokumentasikan perilaku yang dituju, Anda perlu diingat bahwa sebagian besar Anda yang peduli tentang pernyataan DML yang sepenuhnya terkandung dalam prosedur tersimpan Anda. Orang-orang pendukung tidak peduli. Pengembang masa depan mungkin tidak menyadari keinginan Anda untuk memiliki semua DML yang dienkapsulasi dalam prosedur yang tersimpan, atau peduli bahkan jika mereka tahu. Dan siapa pun yang mempertahankan DB ini setelah Anda pergi (baik proyek atau pekerjaan lain) mungkin tidak peduli, atau mungkin tidak dapat mencegah penggunaan ORM tidak peduli berapa banyak mereka memprotes. Jadi, Defaults, dapat membantu karena mereka memberi orang "keluar" saat melakukan INSERT
, terutama ad-hocINSERT
dilakukan oleh perwakilan dukungan, karena itu adalah satu kolom yang tidak perlu mereka sertakan (itulah sebabnya saya selalu menggunakan default pada audit kolom tanggal).
DAN, hanya terpikir oleh saya bahwa itu dapat ditampilkan agak objektif apakah DEFAULT
diperiksa atau tidak ketika kolom terkait hadir dalam INSERT
pernyataan: cukup berikan nilai yang tidak valid. Tes berikut tidak hanya itu:
-- DROP TABLE #BadDefault;
CREATE TABLE #BadDefault
(
[BadDefaultID] INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
[SomeInt] INT NOT NULL DEFAULT (1 / 0)
);
INSERT INTO #BadDefault ([SomeInt]) VALUES (1234); -- Success!!!
SELECT * FROM #BadDefault; -- just to be sure ;-)
INSERT INTO #BadDefault ([SomeInt]) VALUES (DEFAULT); -- Error:
/*
Msg 8134, Level 16, State 1, Line xxxxx
Divide by zero error encountered.
The statement has been terminated.
*/
SELECT * FROM #BadDefault; -- just to be sure ;-)
GO
Seperti yang Anda lihat, ketika kolom (dan nilai, bukan kata kunci DEFAULT
) diberikan, Defaultnya adalah 100% diabaikan. Kami tahu ini karena INSERT
berhasil. Tetapi jika Default digunakan, ada kesalahan karena akhirnya dieksekusi.
Apakah ada cara untuk menghindari batasan DEFAULT dalam eksekusi pemicu?
Meskipun perlu menghindari Batasan Default (setidaknya dalam konteks ini) sama sekali tidak perlu, demi kelengkapan, dapat dicatat bahwa hanya mungkin untuk "menghindari" Kendala Default dalam INSTEAD OF
Pemicu, tetapi tidak dalam AFTER
Pemicu. Menurut dokumentasi untuk CREATE TRIGGER :
Jika ada kendala pada tabel pemicu, mereka diperiksa setelah BUKAN eksekusi pemicu dan sebelum eksekusi pemicu SETELAH. Jika kendala dilanggar, BUKAN tindakan pemicu dibatalkan dan pemicu SETELAH tidak dipecat.
Tentu saja, menggunakan INSTEAD OF
Pemicu akan membutuhkan:
- Menonaktifkan Batasan Default
- Membuat
AFTER
Trigger yang memungkinkan Constraint
Namun, saya tidak akan merekomendasikan hal ini.