Tôi giả sử rằng cơ sở dữ liệu luôn kiểm tra các giá trị mặc định, ngay cả khi tôi cung cấp giá trị chính xác, do đó tôi đang thực hiện cùng một công việc hai lần.
Ừm, tại sao bạn lại cho rằng? ;-). Cho rằng Mặc định tồn tại để cung cấp một giá trị khi cột mà chúng được gắn vào không có trong INSERT
câu lệnh, tôi sẽ giả sử ngược lại: rằng chúng bị bỏ qua hoàn toàn nếu cột liên quan có mặt trong INSERT
câu lệnh.
May mắn thay, không ai trong chúng ta cần phải thừa nhận bất cứ điều gì do câu nói này trong câu hỏi:
Tôi chủ yếu quan tâm đến hiệu suất.
Các câu hỏi về hiệu suất gần như luôn luôn có thể kiểm tra. Vì vậy, chúng tôi chỉ cần đưa ra một thử nghiệm để cho phép SQL Server (cơ quan thực sự ở đây) trả lời câu hỏi này.
THIẾT LẬP
Chạy như sau một lần:
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;
Thực hiện các thử nghiệm riêng lẻ 1A và 1B, không cùng nhau vì điều đó làm lệch thời gian. Chạy từng cái một vài lần để có cảm giác về thời gian trung bình cho mỗi cái.
Kiểm tra 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
Kiểm tra 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
Thực hiện các thử nghiệm 2A và 2B riêng lẻ, không cùng nhau vì điều đó làm lệch thời gian. Chạy từng cái một vài lần để có cảm giác về thời gian trung bình cho mỗi cái.
Kiểm tra 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);
Kiểm tra 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);
Bạn sẽ thấy rằng không có sự khác biệt thực sự về thời gian giữa các thử nghiệm 1A và 1B, hoặc giữa các thử nghiệm 2A và 2B. Vì vậy, không, không có hình phạt hiệu suất để có một DEFAULT
xác định nhưng không được sử dụng.
Ngoài ra, bên cạnh việc chỉ ghi lại hành vi dự định, bạn cần lưu ý rằng chủ yếu là bạn quan tâm đến các câu lệnh DML được chứa hoàn toàn trong các thủ tục được lưu trữ của bạn. Hỗ trợ mọi người không quan tâm. Các nhà phát triển trong tương lai có thể không nhận thức được mong muốn của bạn để có tất cả DML được gói gọn trong các quy trình được lưu trữ đó hoặc quan tâm ngay cả khi họ biết. Và bất cứ ai duy trì DB này sau khi bạn đi (dự án hoặc công việc khác) có thể không quan tâm, hoặc có thể không thể ngăn chặn việc sử dụng ORM cho dù họ có phản đối bao nhiêu. Vì vậy, Mặc định, có thể giúp họ cung cấp cho mọi người "hết" khi thực hiện INSERT
, đặc biệt là quảng cáo INSERT
được thực hiện bởi một đại diện hỗ trợ, vì đó là một cột họ không cần đưa vào (đó là lý do tại sao tôi luôn sử dụng mặc định cho kiểm toán cột ngày).
VÀ, điều đó xảy ra với tôi rằng nó có thể được hiển thị khá khách quan cho dù a DEFAULT
có được kiểm tra hay không khi cột liên quan có mặt trong INSERT
câu lệnh: chỉ đơn giản cung cấp một giá trị không hợp lệ. Bài kiểm tra sau đây thực hiện điều đó:
-- 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
Như bạn có thể thấy, khi một cột (và một giá trị, không phải từ khóa DEFAULT
) được cung cấp, Mặc định sẽ bị bỏ qua 100%. Chúng tôi biết điều này bởi vì những người INSERT
thành công. Nhưng nếu Mặc định được sử dụng, sẽ xảy ra lỗi vì cuối cùng nó cũng được thực thi.
Có cách nào để tránh ràng buộc DEFAULT trong khi thực thi kích hoạt không?
Mặc dù cần phải tránh các ràng buộc mặc định (ít nhất là trong bối cảnh này) là hoàn toàn không cần thiết, vì để hoàn thiện, có thể lưu ý rằng chỉ có thể "tránh" một ràng buộc mặc định trong một INSTEAD OF
Trình kích hoạt, nhưng không phải trong AFTER
Trình kích hoạt. Theo tài liệu cho CREATE TRIGGER :
Nếu các ràng buộc tồn tại trên bảng kích hoạt, chúng sẽ được kiểm tra sau khi thực thi kích hoạt INSTEAD OF và trước khi thực thi kích hoạt SAU. Nếu các ràng buộc bị vi phạm, các hành động kích hoạt INSTEAD OF sẽ được khôi phục và kích hoạt SAU không được kích hoạt.
Tất nhiên, sử dụng INSTEAD OF
Trình kích hoạt sẽ yêu cầu:
- Vô hiệu hóa ràng buộc mặc định
- Tạo
AFTER
Trình kích hoạt cho phép ràng buộc
Tuy nhiên, tôi không chính xác khuyên bạn nên làm điều này.