我假设即使我提供了正确的值,数据库也总是检查默认值,因此我将两次执行相同的工作。
嗯,你为什么要这样?;-)。假设存在默认值,当INSERT
语句所不包含它们所附加的列时,就会提供一个值,我将假设完全相反:如果INSERT
语句中存在关联的列,则它们将被完全忽略。
幸运的是,由于问题中的这一陈述,我们都不需承担任何责任:
我对性能最感兴趣。
关于性能的问题几乎总是可以测试的。因此,我们只需要提出一个测试,以允许SQL Server(这里的真正权威)回答这个问题。
设定
运行以下命令一次:
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;
分别执行测试1A和1B,而不是一起执行,因为这会扭曲时间。每次运行几次,以了解每个定时的平均时间。
测试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
测试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
分别执行测试2A和2B,而不是一起执行,因为这会导致计时不正确。每次运行几次,以了解每个定时的平均时间。
测试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);
测试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);
您应该看到测试1A和1B之间或测试2A和2B之间的时序没有真正的区别。因此,没有,没有DEFAULT
定义但不使用会降低性能。
此外,除了仅记录预期的行为外,您还需要记住,多数情况下您是在乎DML语句完全包含在存储过程中。支持者不在乎。未来的开发人员可能不会意识到您希望将所有DML封装在这些存储过程中的愿望,或者即使他们知道也不会在意。在您离开后维护该数据库的人(另一个项目或工作)可能不在乎,或者不管他们提出多少抗议,都可能无法阻止使用ORM。因此,默认值可以帮助他们在进行INSERT
,特别是临时操作时给人们一个“机会”。INSERT
是由支持代表,因为这是他们无需包括的一列(这就是为什么我总是在审计中使用Defaults日期列)。
而且,我刚想到,DEFAULT
当INSERT
语句中存在关联的列时,可以相当客观地显示是否检查a :只需提供一个无效值即可。以下测试就是这样做的:
-- 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
如您所见,当提供一列(以及一个值,而不是关键字DEFAULT
)时,默认值被100%忽略。我们知道这是因为INSERT
成功。但是,如果使用默认值,则最终执行时会出现错误。
有没有办法避免在触发器执行中使用DEFAULT约束?
尽管需要避免默认约束(至少在此情况下)是完全没有必要的,但为完整起见,应注意的是,只能在INSTEAD OF
触发器内“避免”默认约束,而不能在AFTER
触发器内“避免”默认约束。根据CREATE TRIGGER的文档:
如果触发器表上存在约束,则在执行INSTEAD OF触发器之后和执行AFTER触发器之前检查约束。如果违反了约束,则会回退INSTEAD OF触发器动作,并且不会触发AFTER触发器。
当然,使用INSTEAD OF
触发器需要:
- 禁用默认约束
- 创建一个
AFTER
启用约束的触发器
但是,我不建议您这样做。