Presumo che il database stia controllando sempre i valori predefiniti, anche se fornisco il valore corretto, quindi sto facendo lo stesso lavoro due volte.
Ehm, perché dovresti assumerlo? ;-). Dato che i valori predefiniti esistono per fornire un valore quando la colonna a cui sono collegati non è presente INSERT
nell'istruzione, assumerei l'esatto contrario: che sono completamente ignorati se la colonna associata è presente INSERT
nell'istruzione.
Fortunatamente, nessuno di noi ha bisogno di assumere nulla a causa di questa affermazione nella domanda:
Sono principalmente interessato alle prestazioni.
Le domande sulle prestazioni sono quasi sempre verificabili. Quindi dobbiamo solo elaborare un test per consentire a SQL Server (la vera autorità qui) di rispondere a questa domanda.
IMPOSTARE
Eseguire quanto segue una volta:
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;
Esegui i test 1A e 1B singolarmente, non insieme poiché ciò distorce i tempi. Esegui ciascuno più volte per avere un'idea della tempistica media di ognuno.
Test 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
Test 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
Esegui i test 2A e 2B singolarmente, non insieme in quanto distorce i tempi. Esegui ciascuno più volte per avere un'idea della tempistica media di ognuno.
Test 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);
Test 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);
Dovresti vedere che non c'è una vera differenza nei tempi tra i test 1A e 1B o tra i test 2A e 2B. Quindi, no, non vi è alcuna penalità prestazionale per avere un DEFAULT
definito ma non utilizzato.
Inoltre, oltre a documentare semplicemente il comportamento previsto, è necessario tenere presente che è in gran parte a te che importa che le istruzioni DML siano completamente contenute nelle procedure memorizzate. Alle persone di supporto non importa. I futuri sviluppatori potrebbero non essere consapevoli del tuo desiderio di avere tutto il DML incapsulato all'interno di quelle procedure memorizzate, o preoccuparti anche se lo sanno. E chiunque manterrà questo DB dopo che te ne sarai andato (o un altro progetto o lavoro) potrebbe non interessarti o potrebbe non essere in grado di impedire l'uso di un ORM, non importa quanto protestino. Quindi, Predefiniti, possono aiutare a dare alle persone un "out" quando fanno un INSERT
, specialmente un fatto ad-hoc INSERT
fatto da un rappresentante del supporto, in quanto quella è una colonna che non è necessario includere (motivo per cui uso sempre i valori predefiniti durante il controllo colonne della data).
E, mi è appena venuto in mente che può essere mostrato in modo piuttosto oggettivo se un DEFAULT
è verificato o meno quando la colonna associata è presente INSERT
nell'istruzione: fornire semplicemente un valore non valido. Il seguente test fa proprio questo:
-- 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
Come puoi vedere, quando DEFAULT
viene fornita una colonna (e un valore, non la parola chiave ), l'impostazione predefinita viene ignorata al 100%. Lo sappiamo perché ci INSERT
riesce. Ma se viene utilizzato il valore predefinito, si verifica un errore in quanto viene infine eseguito.
C'è un modo per evitare il vincolo DEFAULT all'interno di un'esecuzione trigger?
Sebbene la necessità di evitare vincoli predefiniti (almeno in questo contesto) sia del tutto superflua, per completezza si può notare che sarebbe solo possibile "evitare" un vincolo predefinito all'interno di un INSTEAD OF
trigger, ma non all'interno di un AFTER
trigger. Secondo la documentazione per CREATE TRIGGER :
Se esistono vincoli sulla tabella dei trigger, questi vengono controllati dopo l'esecuzione del trigger INSTEAD OF e prima dell'esecuzione del trigger AFTER. Se i vincoli vengono violati, le azioni del trigger INSTEAD OF vengono annullate e il trigger AFTER non viene attivato.
Naturalmente, l'utilizzo di un INSTEAD OF
trigger richiederebbe:
- Disabilitazione del vincolo predefinito
- Creazione di un
AFTER
trigger che abilita il vincolo
Tuttavia, non consiglierei esattamente di farlo.