McN*_*ets 20 performance database-design sql-server t-sql default-value query-performance
我通常按照以下规则设计我的数据库:
CREATE TRIGGER <TriggerName>
ON <MyTable>
[BEFORE | AFTER] INSERT
AS
IF EXISTS (SELECT 1
FROM inserted
WHERE Field1 <> <some_initial_value>
OR Field2 <> <other_initial_value>)
BEGIN
UPDATE MyTable
SET Field1 = <some_initial_value>,
Field2 = <other_initial_value>
...
END
Run Code Online (Sandbox Code Playgroud)
sp_MyTable_Insert(@Field1, @Field2, @Field3, ...);
sp_MyTable_Delete(@Key1, @Key2, ...);
sp_MyTable_Update(@Key1, @Key2, @Field3, ...);
Run Code Online (Sandbox Code Playgroud)
您认为,在这种情况下,使用 DEFAULT CONSTRAINT 是否值得,或者我正在向数据库服务器添加额外且不必要的工作?
更新
我知道通过使用 DEFAULT 约束,我向必须管理数据库的其他人提供了更多信息。但我最感兴趣的是性能。
我假设数据库总是检查默认值,即使我提供了正确的值,因此我做了两次相同的工作。
例如,有没有办法在触发器执行中避免 DEFAULT 约束?
Sol*_*zky 24
我假设数据库总是检查默认值,即使我提供了正确的值,因此我做了两次相同的工作。
嗯,你为什么会这么认为?;-) 鉴于 Defaults 存在以在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;
Run Code Online (Sandbox Code Playgroud)
单独执行测试 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
Run Code Online (Sandbox Code Playgroud)
测试 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
Run Code Online (Sandbox Code Playgroud)
单独执行测试 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);
Run Code Online (Sandbox Code Playgroud)
测试 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);
Run Code Online (Sandbox Code Playgroud)
您应该看到测试 1A 和 1B 之间或测试 2A 和 2B 之间的时间没有真正的差异。所以,不,没有DEFAULT定义但未使用的性能损失。
此外,除了仅仅记录预期的行为之外,您还需要记住,主要是您关心 DML 语句是否完全包含在您的存储过程中。支持的人不在乎。未来的开发人员可能不知道您希望将所有 DML 封装在这些存储过程中,或者即使他们知道也不会关心。并且在您离开后(另一个项目或工作)维护此数据库的人可能不在乎,或者无论他们如何抗议都可能无法阻止使用 ORM。因此,默认值可以提供帮助,因为他们在执行时给人们一个“退出” INSERT,尤其是INSERT由支持代表完成的临时工作,因为这是他们不需要包含的一列(这就是为什么我总是在审计中使用默认值日期列)。
并且,我突然想到,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
Run Code Online (Sandbox Code Playgroud)
如您所见,当提供一列(和一个值,而不是关键字DEFAULT)时,默认值会被 100% 忽略。我们知道这一点,因为INSERT成功了。但是如果使用默认值,则在最终执行时会出现错误。
有没有办法在触发器执行中避免 DEFAULT 约束?
虽然需要避免默认约束(至少在这种情况下)是完全没有必要的,但为了完整起见,可以注意到只能“避免”INSTEAD OF触发器内的默认约束,而不能“避免”触发器内AFTER。根据CREATE TRIGGER的文档:
如果触发器表上存在约束,则在 INSTEAD OF 触发器执行之后和 AFTER 触发器执行之前检查它们。如果违反约束,则 INSTEAD OF 触发器操作将回滚并且不会触发 AFTER 触发器。
当然,使用INSTEAD OF触发器需要:
AFTER启用约束的触发器但是,我不完全推荐这样做。
我认为默认约束没有什么大的害处。事实上,我看到了一个特别的优势 - 您已经在与表定义本身相同的逻辑级别定义了默认值。如果您在存储过程中提供了默认值,则必须有人去那里找出默认值是什么;而且,对于系统的新手来说,这不是很明显的事情,必然(例如,如果,例如,您明天继承了 10 亿美元,购买了自己的热带岛屿,然后退出并搬到那里,留下一些其他可怜的傻瓜来解决问题自己出)。
| 归档时间: |
|
| 查看次数: |
4415 次 |
| 最近记录: |