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 INSERTpernyataan, 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 DEFAULTditentukan 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 DEFAULTdiperiksa atau tidak ketika kolom terkait hadir dalam INSERTpernyataan: 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 INSERTberhasil. 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 OFPemicu, tetapi tidak dalam AFTERPemicu. 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 OFPemicu akan membutuhkan:
- Menonaktifkan Batasan Default
- Membuat
AFTERTrigger yang memungkinkan Constraint
Namun, saya tidak akan merekomendasikan hal ini.