Supongo que la base de datos verifica siempre los valores predeterminados, incluso si proporciono el valor correcto, por lo tanto, estoy haciendo el mismo trabajo dos veces.
¿Por qué asumirías eso? ;-). Dado que los valores predeterminados existen para proporcionar un valor cuando la columna a la que están unidos no está presente en la INSERT
declaración, asumiría exactamente lo contrario: que se ignoran por completo si la columna asociada está presente en la INSERT
declaración.
Afortunadamente, ninguno de nosotros necesita asumir nada debido a esta declaración en la pregunta:
Estoy principalmente interesado en el rendimiento.
Las preguntas sobre el rendimiento son casi siempre comprobables. Por lo tanto, solo necesitamos una prueba para permitir que SQL Server (la verdadera autoridad aquí) responda esta pregunta.
PREPARAR
Ejecute lo siguiente una vez:
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;
Ejecute las pruebas 1A y 1B individualmente, no juntas, ya que eso sesga el tiempo. Ejecute cada uno varias veces para tener una idea del tiempo promedio para cada uno.
Prueba 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
Prueba 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
Ejecute las pruebas 2A y 2B individualmente, no juntas, ya que eso sesga el tiempo. Ejecute cada uno varias veces para tener una idea del tiempo promedio para cada uno.
Prueba 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);
Prueba 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);
Debería ver que no hay una diferencia real en el tiempo entre las pruebas 1A y 1B, o entre las pruebas 2A y 2B. Entonces, no, no hay penalización de rendimiento para tener un DEFAULT
definido pero no utilizado.
Además, además de simplemente documentar el comportamiento previsto, debe tener en cuenta que es principalmente a usted a quien le importa que las declaraciones DML estén completamente contenidas en sus procedimientos almacenados. La gente de apoyo no les importa. Es posible que los futuros desarrolladores no sean conscientes de su deseo de tener todo el DML encapsulado dentro de esos procedimientos almacenados, o se preocupen incluso si lo saben. Y a quien sea que mantenga este DB después de que usted se haya ido (ya sea otro proyecto o trabajo) puede que no le importe o que no pueda evitar el uso de un ORM sin importar cuánto protesten. Por lo tanto, los valores predeterminados pueden ayudar, ya que le dan a la gente un "out" cuando hacen un INSERT
, especialmente un ad-hoc INSERT
hecho por un representante de soporte, ya que esa es una columna que no necesitan incluir (por eso siempre uso los valores predeterminados en la auditoría columnas de fecha).
Y, se me ocurrió que se puede mostrar de manera bastante objetiva si DEFAULT
se verifica o no cuando la columna asociada está presente en la INSERT
declaración: simplemente proporcione un valor no válido. La siguiente prueba hace exactamente eso:
-- 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
Como puede ver, cuando se proporciona una columna (y un valor, no la palabra clave DEFAULT
), el valor predeterminado se ignora al 100%. Lo sabemos porque INSERT
tiene éxito. Pero si se usa el valor predeterminado, hay un error, ya que finalmente se está ejecutando.
¿Hay alguna manera de evitar la restricción DEFAULT dentro de una ejecución de disparo?
Si bien es necesario evitar las Restricciones predeterminadas (al menos en este contexto) es completamente innecesario, en aras de la exhaustividad se puede observar que solo sería posible "evitar" una Restricción predeterminada dentro de un INSTEAD OF
Disparador, pero no dentro de un AFTER
Disparador. De acuerdo con la documentación de CREATE TRIGGER :
Si existen restricciones en la tabla de desencadenadores, se verifican después de la ejecución del desencadenador INSTEAD OF y antes de la ejecución del desencadenador DESPUÉS. Si se violan las restricciones, las acciones del disparador INSTEAD OF se deshacen y el disparador DESPUÉS no se dispara.
Por supuesto, usar un INSTEAD OF
disparador requeriría:
- Deshabilitar la restricción predeterminada
- Crear un
AFTER
disparador que habilite la restricción
Sin embargo, no recomendaría exactamente hacer esto.