Je suppose que la base de données vérifie toujours les valeurs par défaut, même si je fournis la valeur correcte, donc je fais le même travail deux fois.
Pourquoi supposeriez-vous cela? ;-). Étant donné que les valeurs par défaut existent pour fournir une valeur lorsque la colonne à laquelle elles sont attachées n'est pas présente dans la INSERT
déclaration, je suppose que c'est exactement le contraire: qu'elles sont complètement ignorées si la colonne associée est présente dans la INSERT
déclaration.
Heureusement, aucun de nous n'a à supposer quoi que ce soit en raison de cette déclaration dans la question:
Je m'intéresse principalement à la performance.
Les questions sur les performances sont presque toujours testables. Il nous suffit donc de proposer un test pour permettre à SQL Server (la véritable autorité ici) de répondre à cette question.
INSTALLER
Exécutez la commande suivante une fois:
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;
Exécutez les tests 1A et 1B individuellement, pas ensemble car cela biaise le timing. Exécutez chacun plusieurs fois pour avoir une idée de la durée moyenne de chacun.
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
Exécutez les tests 2A et 2B individuellement, pas ensemble car cela biaise le timing. Exécutez chacun plusieurs fois pour avoir une idée de la durée moyenne de chacun.
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);
Vous devriez voir qu'il n'y a pas de réelle différence de timing entre les tests 1A et 1B, ou entre les tests 2A et 2B. Donc, non, il n'y a pas de pénalité de performance pour avoir un DEFAULT
défini mais pas utilisé.
De plus, en plus de simplement documenter le comportement prévu, vous devez garder à l'esprit que c'est surtout vous qui vous souciez que les instructions DML soient complètement contenues dans vos procédures stockées. Les gens de soutien s'en moquent. Les futurs développeurs pourraient ne pas être conscients de votre désir d'avoir tous les DML encapsulés dans ces procédures stockées, ou s'en soucier même s'ils le savent. Et quiconque maintient cette base de données après votre départ (soit un autre projet ou un autre travail) peut ne pas s'en soucier, ou ne pas être en mesure d'empêcher l'utilisation d'un ORM, peu importe combien ils protestent. Par conséquent, les valeurs par défaut peuvent aider en ce sens qu'elles donnent aux gens un "out" lors de la réalisation d'un INSERT
, en particulier un ad-hoc INSERT
effectué par un représentant du support, car il s'agit d'une colonne qu'ils n'ont pas besoin d'inclure (c'est pourquoi j'utilise toujours les valeurs par défaut lors de l'audit). colonnes de date).
ET, il m'est venu à l'esprit qu'il peut être montré de manière assez objective si oui ou non DEFAULT
est vérifiée lorsque la colonne associée est présente dans l' INSERT
instruction: fournissez simplement une valeur non valide. Le test suivant fait exactement cela:
-- 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
Comme vous pouvez le voir, lorsqu'une colonne (et une valeur, pas le mot-clé DEFAULT
) est fournie, la valeur par défaut est 100% ignorée. Nous le savons parce que cela INSERT
réussit. Mais si la valeur par défaut est utilisée, il y a une erreur car elle est finalement en cours d'exécution.
Existe-t-il un moyen d'éviter la contrainte DEFAULT dans une exécution de déclencheur?
Bien qu'il soit nécessaire d'éviter les contraintes par défaut (au moins dans ce contexte), pour des raisons d'exhaustivité, on peut noter qu'il ne serait possible "d'éviter" une contrainte par défaut dans un INSTEAD OF
déclencheur, mais pas dans un AFTER
déclencheur. Selon la documentation de CREATE TRIGGER :
S'il existe des contraintes sur la table de déclenchement, elles sont vérifiées après l'exécution du déclencheur INSTEAD OF et avant l'exécution du déclencheur AFTER. Si les contraintes sont violées, les actions de déclenchement INSTEAD OF sont annulées et le déclencheur AFTER n'est pas déclenché.
Bien sûr, l'utilisation d'un INSTEAD OF
déclencheur nécessiterait:
- Désactivation de la contrainte par défaut
- Création d'un
AFTER
déclencheur qui active la contrainte
Cependant, je ne recommanderais pas exactement cela.