Я припускаю, що база даних перевіряє завжди значення за замовчуванням, навіть якщо я надаю правильне значення, отже, я виконую ту саму роботу двічі.
Гм, навіщо ти це припускати? ;-). Зважаючи на те, що за замовчуванням існують значення, коли стовпець, до якого вони прикріплені, немає у 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 окремо, а не разом, оскільки це перекручує терміни. Запустіть кожен кілька разів, щоб зрозуміти середній час для кожного.
Тест 1А
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
Тест 1В
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 окремо, а не разом, оскільки це перекручує терміни. Запустіть кожен кілька разів, щоб зрозуміти середній час для кожного.
Тест 2А
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);
Тест 2В
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);
Ви повинні побачити, що між тестами 1А та 1Б або між тестами 2А та 2Б немає реальної різниці в термінах. Отже, ні, немає пені за ефективність, щоб мати DEFAULT
визначений, але не використаний.
Крім того, окрім того, що просто документувати призначене поведінку, потрібно пам’ятати, що саме ви дбаєте про те, щоб висловлювання DML повністю містилися у ваших збережених процедурах. Підтримка людей не хвилює. Майбутні розробники, можливо, не знають про ваше бажання зробити весь DML інкапсульованим у межах цих збережених процедур, або небайдуже, навіть якщо вони це знають. І той, хто підтримує цю БД після того, як вас немає (будь-який інший проект чи робота), може не хвилюватись, або не зможе запобігти використанню ОРМ, незалежно від того, наскільки вони протестують. Таким чином, за замовчуванням вони можуть допомогти тим, що вони дають людям "вийти" під час виконання INSERT
, особливо спеціального, INSERT
зробленого службою підтримки, оскільки це один стовпець, який вони не потрібно включати (саме тому я завжди використовую за замовчуванням аудит стовпці дати).
І мені просто спало на думку, що можна досить об'єктивно показати, перевіряється чи ні DEFAULT
, коли асоційований стовпець присутній у INSERT
виписці: просто введіть недійсне значення. Наступний тест робить саме це:
-- 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 тригера відкочуються назад, і тригер ПІСЛЯ не спрацьовує.
Звичайно, використання INSTEAD OF
тригера зажадає:
- Вимкнення обмеження за замовчуванням
- Створення
AFTER
тригера, який дозволяє обмежити
Однак я б точно не рекомендував це робити.