翻譯|使用教程|編輯:楊鵬連|2020-07-03 17:04:38.327|閱讀 376 次
概述:如果您的某些數據庫約束具有系統生成的名稱,則在比較架構和使用SQL Compare或SQL Change Automation生成構建腳本時,它們可能導致“誤報”。Phil Factor解釋了這些困難,并提供了啟用“比較”選項可以避免這些困難。
# 界面/圖表報表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
SQL Compare是一款比較和同步SQL Server數據庫結構的工具。現有超過150,000的數據庫管理員、開發人員和測試人員在使用它。當測試本地數據庫,暫存或激活遠程服務器的數據庫時,SQL Compare將分配數據庫的過程自動化。
盡管您可以在表中命名鍵和約束,但這不是強制性的。當您創建表構建腳本并將代碼的簡寫形式用于約束而不給出名稱時,SQL Server合并組成一個名稱,每次都不同。如果在導入數據時刪除并重新創建約束,它們將獲得新名稱。
在創建或更改表時DEFAULT,很少有人會為UNIQUE,和CHECK約束命名。如果您充分使用這些具有顯式,永久名稱的出色設備,則表腳本可能會變得混亂。不一定總是指定偶數PRIMARY KEY或FOREIGN KEY約束,而SQL語法允許這樣做。所有這一切都沒有什么特別的錯誤,因為在正常使用中,您無需按名稱引用約束。
在某些情況下,系統生成的名稱可能會帶來額外的困難。當使用SQL Compare或SQL Change Automation比較數據庫時,您可能會找到一個示例。除非有特別指示,否則這兩個工具都將通過一個選項將這兩個不斷變化的系統生成的名稱分配給沒有使用的已定義名稱的約束或鍵。
為了證明這一點,讓我們做一個快速的實驗,以說明為什么我使用的短語“不斷變化”會在經驗豐富的數據庫開發人員中敲響警鐘。
識別系統命名的約束
系統命名的約束沒有SQL代碼的味道,盡管它們可能使某些人不知道。您可以確定數據庫中是否包含系統命名的約束。
SELECT name AS System_named_Constraint, Object_Schema_Name(parent_object_id) + '.' + Object_Name(parent_object_id) AS TheTable, TheType FROM ( SELECT name, parent_object_id, 'check constraint' FROM sys.check_constraints AS CC WHERE is_system_named <> 0 UNION ALL SELECT name, parent_object_id, 'Default constraint' FROM sys.default_constraints AS DC WHERE is_system_named <> 0 UNION ALL SELECT name, parent_object_id, 'Key constraint' FROM sys.key_constraints AS KC WHERE is_system_named <> 0 ) AS f(name, parent_object_id, TheType);
這段代碼給出了我們將要用作測試的數據庫:
默認情況下,SQL Compare如何處理系統命名的約束
首先,我們將創建一個使用系統命名約束的小數據庫:我們將使用舊的Pubs構建腳本。從中我們將顯示兩個表publishers和titles,僅用于說明(我已經用注釋標記了系統命名的約束):
CREATE TABLE publishers ( pub_id CHAR(4) NOT NULL CONSTRAINT UPKCL_pubind PRIMARY KEY CLUSTERED --named constraint CHECK (pub_id IN ('1389', '0736', '0877', '1622', '1756') OR pub_id LIKE '99[0-9][0-9]' ),--system-named constraint pub_name VARCHAR(40) NULL, city VARCHAR(20) NULL, state CHAR(2) NULL, country VARCHAR(30) NULL DEFAULT ('USA')--system-named constraint ); GO CREATE TABLE titles ( title_id tid CONSTRAINT UPKCL_titleidind PRIMARY KEY CLUSTERED, title VARCHAR(80) NOT NULL, type CHAR(12) NOT NULL DEFAULT ('UNDECIDED'),--system-named constraint pub_id CHAR(4) NULL REFERENCES publishers (pub_id),--system-named constraint price MONEY NULL, advance MONEY NULL, royalty INT NULL, ytd_sales INT NULL, notes VARCHAR(200) NULL, pubdate DATETIME NOT NULL DEFAULT (GetDate())--system-named constraint ); GO我們可以運行完整的構建腳本,然后在SSMS中使用“ 任務” >“ 生成腳本”從該數據庫生成一個新的構建腳本。我們會發現,對于沒有用戶定義名稱的任何約束,SSMS會繼續在其生成的生成腳本中繼續使用速記“無名稱” SQL語法。如果希望它生成包含系統生成的名稱的腳本,則必須打開“包括系統約束名稱”選項。
ALTER TABLE [dbo].[publishers] ADD DEFAULT ('USA') FOR [country] GO ALTER TABLE [dbo].[titles] ADD DEFAULT ('UNDECIDED') FOR [type] GO ALTER TABLE [dbo].[titles] ADD DEFAULT (getdate()) FOR [pubdate] GO ALTER TABLE [dbo].[titles] WITH CHECK ADD FOREIGN KEY([pub_id]) REFERENCES [dbo].[publishers] ([pub_id]) GO ALTER TABLE [dbo].[publishers] WITH CHECK ADD CHECK (([pub_id]='1756' OR [pub_id]='1622' OR [pub_id]='0877' OR [pub_id]='0736' OR [pub_id]='1389' OR [pub_id] like '99[0-9][0-9]')) GO現在,我們將使用SQL Compare 通過使用所有默認的Compare選項,通過將它與空的目標數據庫進行比較,從原始Pubs數據庫的同一副本中生成構建腳本。在Publishers表中,DEFAULTfor Country列的突然有了一個名稱:
[country] [varchar] (30) NULL CONSTRAINT [DF__publisher__count__3D5E1FD2] DEFAULT ('USA')在CHECK上約束Pub_ID的Publishers表成為
ALTER TABLE [dbo].[publishers] ADD CONSTRAINT [CK__publisher__pub_i__3C69FB99] CHECK (([pub_id]='1756' OR [pub_id]='1622' OR [pub_id]='0877' OR [pub_id]='0736' OR [pub_id]='1389' OR [pub_id] like '99[0-9][0-9]'))因此,我們可以看到Publishers表突然出現了名為defaults和key的表。該Titles表也是如此。
[type] [char] (12) COLLATE Latin1_General_CI_AS NOT NULL CONSTRAINT [DF__titles__type__403A8C7D] DEFAULT ('UNDECIDED'), , [pubdate] [datetime] NOT NULL CONSTRAINT [DF__titles__pubdate__4222D4EF] DEFAULT (getdate())后來,它FOREIGN KEY添加了它的約束,因此我們可以看到名稱發生了什么。
ALTER TABLE [dbo].[titles] ADD CONSTRAINT [FK__titles__pub_id__412EB0B6] FOREIGN KEY ([pub_id]) REFERENCES [dbo].[publishers] ([pub_id])
SQL Compare已使用系統生成的名稱將所有以系統命名的對象轉換為用戶命名的對象。通過將名稱應用于以系統命名的對象,它實際上已經錯誤地表示了數據庫。它不必這樣做,但是它是默認的“ Redgate”選項。
如果您總是通過簡單地更改現有對象來開發數據庫,那么這不太可能。但是,通過使用默認選項,您在隨后比較表以查看是否有任何更改時會遇到麻煩。您還限制了SQL Compare生成的任何腳本的值。我會舉例說明。相同的數據庫,相同的腳本,相同的服務器
讓我們嘗試另一個實驗。這次,我們將在同一臺服務器上使用相同的原始Pubs構建腳本創建Pubs數據庫的新副本。然后,我們使用SQL Compare比較兩個數據庫。令人驚訝的是,它發現它們是相同的,因為SQL Server在執行表DDL代碼時在兩個數據庫中使用相同的算法來生成系統名稱。
相同的數據庫,相同的腳本,不同的服務器
現在,我們在另一個服務器上使用相同的原始Pubs構建腳本創建一個相同的數據庫。這次一切都錯了。我們要做的就是使用其他服務器。這兩個數據庫是相同的,因為它們是由相同的腳本生成的,但是SQL Compare認為表是不同的。
同一數據庫,不同腳本,同一服務器
好吧,當然,我們可以在同一臺服務器上開發,然后呢?我們可以通過另一個測試來證明這一點。如前所述,我們使用原始腳本構建來構建Pubs,使用SSMS從中生成構建腳本,然后使用它來創建名為PubsTest的數據庫的新構建。
最后,我們使用SQL Compare比較兩個數據庫Pubs和PubsTest:
允許SQL Compare將名稱賦予未命名約束的問題
如果將SQL Compare及其默認選項用于檢查表差異,則將得到很多誤報。當唯一的區別是約束的自動系統名稱時,表將被標記為不同。如果兩個開發人員使用腳本工作以在開發它們時偶爾構建表,即使它們實際上是相同的,它們也會在版本控制中被標記為不同,因為這是基于字符串的比較,而不是語義比較。
SQL Compare將生成生成腳本,每次您刪除并重新創建系統命名的約束或從腳本重新創建表時,這些生成腳本都會更改。如果您使用帶有默認選項的SQL Compare,則版本控制系統可以輕松地阻塞微不足道的更改,并且敢于在對表進行更改時重建表。
還有另一種尷尬。在將數據導入表中之前,您通常需要先禁用所有約束,然后重新啟用它們。您可以通過執行以下操作禁用表上的所有約束:
ALTER TABLE MyTable NOCHECK CONSTRAINT ALL然后,您可以使用以下命令打開它們:
ALTER TABLE MyTable WITH CHECK CHECK CONSTRAINT ALL但是,通常,腳本通過按名稱分別標識每個約束來禁用此功能,以禁用檢查,然后在數據導入后啟用檢查。有時,它們會掉落并重新創建它們。在SQL中以外科手術精度單獨訪問約束的最簡單方法是按名稱引用。如果執行此操作,則方便的數據導入例程將在數據庫的一個版本上創建的具有系統生成名稱的約束的名稱指定,該約束是在數據庫的一個版本上創建的,無法在同一服務器上創建的數據庫的精確副本上使用使用不同的腳本。
獲取SQL Compare以忽略系統命名的約束和索引名稱
有一個簡單的解決方案,可以在生成腳本時使Compare的行為與SSMS(和SMO)默認行為保持一致。
默認情況下,SQL Compare的選項“ 忽略系統命名的約束和索引名稱 ”為OFF。您應該為您的SQL Compare副本啟用它并將其設置為默認值。這樣做是為了強制SQL Compare認識到系統命名的約束實際上沒有名稱,因此它必須使用其他方式比較它們。
如果使用sqlserver模塊從SSMS或SMO為對象或整個數據庫生成構建腳本,則它將在腳本中正確表示系統命名的對象。如果打開SQL Compare的' 忽略系統命名的約束和索引名稱 '選項,它將執行相同的操作。您可能會想改為選擇“忽略約束和索引名稱”,但這不能解決此問題。
訪問個人約束
有人說,通過使用系統命名的約束,您使由單個約束引起的錯誤難以理解。實際上,正如我在本文中所展示的那樣,系統生成的名稱是經過專門設計的,以使其易于確定所涉及的表,約束類型甚至列。
也有人說這使得表格難以比較。當然,這需要一種不同的方法。比較表時,您需要根據其作用而不是其名稱來識別和比較以系統命名的約束。如果告訴您,SQL Compare很樂意這樣做。
DEFAULT約束很容易,因為一列只能有一個。如果默認值更改為20到40,則它是一個不同的約束。甲CHECK上的列約束是由什么它確實是這樣,有效地,它執行列和代碼是標識符定義。表級CHECK約束由其代碼定義。一個FOREIGN KEY約束是引用表,引用的表和列的列表中標識。
但是,實際上,很少有理由合理地動態需求DELETE或ALTER約束。通常,您唯一需要訪問約束的時間就是打開或關閉約束。然后,您幾乎總是想一次全部關閉或打開它們。
結論
SQL中未命名約束的想法使一些開發人員和DBA感到震驚,但是如果使人們更容易使用約束,我全力以赴。實際上,我希望有更多的方法來鼓勵使用適當的約束。
我總是在SQL Compare和SCA cmdlet中包含選項“ 忽略系統命名的約束和索引名稱 ”,這是我的默認設置,因此在沒有此設置的情況下使用它時會感到震驚,因為開始發生奇怪的事情。通過包含它,您更有信心使用SQL Compare生成的對象級和數據庫級腳本將與SSMS生成的腳本具有相同的工作方式,但具有錯誤檢查和回滾的好處錯誤,并保存數據。
相關產品推薦:
SQL Prompt:SQL語法提示工具
SQL Toolbelt:Red Gate產品套包
SQL Monitor:SQL Server監控工具
本站文章除注明轉載外,均為本站原創或翻譯。歡迎任何形式的轉載,但請務必注明出處、不得修改原文相關鏈接,如果存在內容上的異議請郵件反饋至chenjj@fc6vip.cn
文章轉載自: