正しい値を指定しても、データベースは常にデフォルト値をチェックしているため、同じジョブを2回実行しています。
ええと、なぜあなたはそれを仮定しますか?;-)。INSERT
ステートメントに列がアタッチされているときに値を提供するためにデフォルトが存在する場合、正反対のことを想定します。つまり、関連する列がINSERT
ステートメントに存在する場合、それらは完全に無視されます。
幸いなことに、私たちはどちらも、質問のこの声明のために何かを仮定する必要はありません。
私は主にパフォーマンスに興味があります。
パフォーマンスに関する質問はほとんど常にテスト可能です。そのため、SQL Server(ここでは真の権限)がこの質問に答えられるようにするためのテストを考え出す必要があります。
セットアップ
次を1回実行します。
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を個別に実行します。タイミングがずれるため、一緒にではありません。それぞれを数回実行して、それぞれの平均タイミングの感覚をつかんでください。
テスト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
テスト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
テスト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);
テスト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);
テスト1Aと1Bの間、またはテスト2Aと2Bの間でタイミングに実質的な違いがないことがわかるはずです。そのため、DEFAULT
定義されているが使用されていないことによるパフォーマンスの低下はありません。
また、意図された動作を単に文書化するだけでなく、DMLステートメントがストアドプロシージャ内に完全に含まれていることを気にするのは、ほとんどあなたであることに留意する必要があります。サポート担当者は気にしません。将来の開発者は、これらのストアドプロシージャ内にすべてのDMLをカプセル化したいという要望を認識していないか、または知っていても気にしないかもしれません。そして、あなたが去った後にこのDBを維持する人(他のプロジェクトや仕事)は、気にしないかもしれませんし、彼らがどれだけ抗議しても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
ご覧のとおり、列(およびキーワードではなく値DEFAULT
)が指定されている場合、デフォルトは100%無視されます。これはINSERT
成功しているからです。ただし、デフォルトが使用されている場合、最終的に実行されるためエラーが発生します。
トリガー実行内でDEFAULT制約を回避する方法はありますか?
(少なくともこのコンテキストでは)デフォルト制約を回避する必要はまったくありませんが、完全を期すために、INSTEAD OF
トリガー内ではなく、トリガー内でのみデフォルト制約を「回避」できることに注意してくださいAFTER
。CREATE TRIGGERのドキュメントによると:
トリガーテーブルに制約が存在する場合、INSTEAD OFトリガーの実行後、AFTERトリガーの実行前に制約がチェックされます。制約に違反すると、INSTEAD OFトリガーアクションはロールバックされ、AFTERトリガーは起動されません。
もちろん、INSTEAD OF
トリガーを使用するには以下が必要です。
- デフォルトの制約を無効にする
AFTER
制約を有効にするトリガーの作成
ただし、これを行うことはお勧めできません。