Sql-Server

我應該只依靠外鍵約束來處理錯誤嗎?

  • September 16, 2016

我一直在使用以下模式來驗證執行 INSERT 的儲存過程的外鍵(FK)參數:

CREATE PROC spCreateChildFoo
   @ForeignKey_Id int,
   @Attribute varchar(50)
AS

IF NOT EXISTS (SELECT ForeignKey_Id FROM ForeignTable WHERE ForeignKey_Id = @ForeignKey_Id)
   BEGIN
       RAISERROR ('The input ForeignKey_Id does not exist', 16, 1)
       RETURN;
   END

INSERT INTO ChildTable (ForeignKey_Id, Attribute)
VALUES (@ForeignKey_Id, @Attribute)

我這樣做是因為我想在插入任何數據之前在執行儲存過程的早期擷取錯誤的 FK 條目,特別是對於執行多個 INSERT 的過程。我還認為這將是返回準確錯誤消息以簡化調試的好方法。這種模式很好地服務於這兩個目標。

問題

  • 這是浪費時間嗎?
  • 更具體地說,我是否應該僅依靠 FK 約束來驗證 FK 參數並在失敗時回滾?
  • 隨著數據庫的增長,它最終會損害我的應用程序的性能嗎?

是的,至少當您的儲存過程只是可能導致外鍵違規的 INSERT 語句時,這是浪費時間。在外鍵存在的情況下,數據庫引擎本身針對參考表對任何嘗試的 FK 值進行驗證。

如果引用表中不存在該值,您將收到異常併中止您的 INSERT 語句。

所以,這就足夠了:

CREATE PROC spCreateChildFoo
   @ForeignKey_Id int,
   @Attribute varchar(50)
AS

INSERT INTO ChildTable (ForeignKey_Id, Attribute)
VALUES (@ForeignKey_Id, @Attribute);

另一方面,如果您的過程不止一個 INSERT 語句,並且您想在開始時對無效插入中止整個過程,那麼初步檢查 FK 值可能並非完全沒有意義。但是,有效的 FK 值仍會導致雙重查找,這既不好也沒有必要。為了達到中止整個過程的相同效果,您可以將過程的主體放入 TRY/CATCH 塊中:

CREATE PROC spCreateChildFoo
   @ForeignKey_Id int,
   @Attribute varchar(50)
AS

BEGIN TRY
   INSERT INTO ChildTable (ForeignKey_Id, Attribute)
   VALUES (@ForeignKey_Id, @Attribute);

   ... -- other statements
END TRY
BEGIN CATCH
   RAISERROR ('The input ForeignKey_Id does not exist', 16, 1);
   -- or you could catch ERROR_NUMBER(), ERROR_MESSAGE() etc.
   -- and re-raise the original exception
END CATCH;

更多資訊:

引用自:https://dba.stackexchange.com/questions/149725