Sql-Server-2016

您如何最大限度地減少臨時表上系統命名約束之間發生衝突的機會?

  • May 21, 2020

當我們的一些程序嘗試使用系統命名的 PK 約束創建臨時表時,我們偶爾會遇到錯誤“數據庫中已經有一個名為 ‘PK__#TempTab__0796211ACE71813B’ 的對象”,如下所示:

CREATE OR ALTER PROCEDURE dbo.StoredProc AS
BEGIN
   CREATE TABLE #TempTable (id INT NOT NULL, PRIMARY KEY CLUSTERED (id))
   ...
END

查詢 tempdb.sys.objects 會發現數百個 PK__#TempTab__XYZ 形式的約束。許多是幾小時前創建的,從那以後就沒有被修改過。當我查看 tempdb.sys.objects 時,活動會話非常少,因此很難相信目前有這麼多臨時表正在使用。我們確實在許多儲存過程中創建了許多類似名稱的臨時表。

我認為臨時表記憶體在這裡負責。這些 PK 約束似乎一直存在於 tempdb 中,直到它們相關的記憶體計劃被刪除。測試表明,禁用臨時表記憶體(創建統計資訊、添加命名約束等)會導致 PK 約束在臨時表超出範圍時從 tempdb.sys.objects 中刪除。觸發重新編譯和刷新 proc 記憶體也會從 tempdb.sys.objects 中清除這些 PK 約束。

我知道系統命名的約束不能保證是唯一的,如本文所述SQL Server 可以在系統生成的約束名稱中創建衝突嗎?.

我的問題是:

  • 我是否錯誤地認為臨時表記憶體會增加臨時表上系統命名約束的名稱衝突的機會?
  • 計劃記憶體膨脹是否會導致我在 tempdb.sys.objects 中看到的大量 PK__#TempTab__XYZ 約束,從而增加兩個約束具有相同名稱的機會?我試圖理解為什麼我們經常看到這種罕見的錯誤。
  • 如果我們不能確保臨時表上的系統命名約束永遠不會遇到命名衝突,我們可以做些什麼來減少這些衝突的機會呢?

最簡單的解決方案是使用唯一的聚集索引,它在系統中不必是唯一的(與主鍵約束不同)。否則,它們的功能大多相同——唯一索引仍然是防止重複的約束,也可以用作外鍵的目標。

我忘記了這種特定的內聯索引語法在 2016 年是否可行(我在本地只有 2019 年),但這在較新的版本上執行良好:

CREATE TABLE #fooA(id int, INDEX PK1 UNIQUE clustered(id));
CREATE TABLE #fooB(id int, INDEX PK1 UNIQUE clustered(id));

如果該語法在 2016 年還無效,您可以在第二步中創建一個唯一的聚集索引,您必須自己命名(但您不必找到一些複雜的方法來使該名稱唯一)。

此外,不要過度使用通用名稱,如#TempTable,使用更特定於表內容/過程功能的名稱(如#Orders#Customers等)。在您需要顯式 PK 的情況下,如果您的所有PK 不再是 system-named ,這至少會減少發生衝突的機會PK__#TempTab_...

順便說一句,我認為我從未在系統中見過如此多的臨時表,以至於在這些長的、試圖成為唯一的名稱上發生衝突。您是否試圖淹沒元數據?

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