翻譯|使用教程|編輯:楊鵬連|2020-07-03 17:04:38.327|閱讀 376 次
概述:如果您的某些數(shù)據(jù)庫約束具有系統(tǒng)生成的名稱,則在比較架構(gòu)和使用SQL Compare或SQL Change Automation生成構(gòu)建腳本時(shí),它們可能導(dǎo)致“誤報(bào)”。Phil Factor解釋了這些困難,并提供了啟用“比較”選項(xiàng)可以避免這些困難。
# 界面/圖表報(bào)表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
SQL Compare是一款比較和同步SQL Server數(shù)據(jù)庫結(jié)構(gòu)的工具。現(xiàn)有超過150,000的數(shù)據(jù)庫管理員、開發(fā)人員和測試人員在使用它。當(dāng)測試本地?cái)?shù)據(jù)庫,暫存或激活遠(yuǎn)程服務(wù)器的數(shù)據(jù)庫時(shí),SQL Compare將分配數(shù)據(jù)庫的過程自動(dòng)化。
盡管您可以在表中命名鍵和約束,但這不是強(qiáng)制性的。當(dāng)您創(chuàng)建表構(gòu)建腳本并將代碼的簡寫形式用于約束而不給出名稱時(shí),SQL Server合并組成一個(gè)名稱,每次都不同。如果在導(dǎo)入數(shù)據(jù)時(shí)刪除并重新創(chuàng)建約束,它們將獲得新名稱。
在創(chuàng)建或更改表時(shí)DEFAULT,很少有人會(huì)為UNIQUE,和CHECK約束命名。如果您充分使用這些具有顯式,永久名稱的出色設(shè)備,則表腳本可能會(huì)變得混亂。不一定總是指定偶數(shù)PRIMARY KEY或FOREIGN KEY約束,而SQL語法允許這樣做。所有這一切都沒有什么特別的錯(cuò)誤,因?yàn)樵谡J褂弥校鸁o需按名稱引用約束。
在某些情況下,系統(tǒng)生成的名稱可能會(huì)帶來額外的困難。當(dāng)使用SQL Compare或SQL Change Automation比較數(shù)據(jù)庫時(shí),您可能會(huì)找到一個(gè)示例。除非有特別指示,否則這兩個(gè)工具都將通過一個(gè)選項(xiàng)將這兩個(gè)不斷變化的系統(tǒng)生成的名稱分配給沒有使用的已定義名稱的約束或鍵。
為了證明這一點(diǎn),讓我們做一個(gè)快速的實(shí)驗(yàn),以說明為什么我使用的短語“不斷變化”會(huì)在經(jīng)驗(yàn)豐富的數(shù)據(jù)庫開發(fā)人員中敲響警鐘。
識(shí)別系統(tǒng)命名的約束
系統(tǒng)命名的約束沒有SQL代碼的味道,盡管它們可能使某些人不知道。您可以確定數(shù)據(jù)庫中是否包含系統(tǒng)命名的約束。
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);
這段代碼給出了我們將要用作測試的數(shù)據(jù)庫:
默認(rèn)情況下,SQL Compare如何處理系統(tǒng)命名的約束
首先,我們將創(chuàng)建一個(gè)使用系統(tǒng)命名約束的小數(shù)據(jù)庫:我們將使用舊的Pubs構(gòu)建腳本。從中我們將顯示兩個(gè)表publishers和titles,僅用于說明(我已經(jīng)用注釋標(biāo)記了系統(tǒng)命名的約束):
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我們可以運(yùn)行完整的構(gòu)建腳本,然后在SSMS中使用“ 任務(wù)” >“ 生成腳本”從該數(shù)據(jù)庫生成一個(gè)新的構(gòu)建腳本。我們會(huì)發(fā)現(xiàn),對(duì)于沒有用戶定義名稱的任何約束,SSMS會(huì)繼續(xù)在其生成的生成腳本中繼續(xù)使用速記“無名稱” SQL語法。如果希望它生成包含系統(tǒng)生成的名稱的腳本,則必須打開“包括系統(tǒng)約束名稱”選項(xiàng)。
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現(xiàn)在,我們將使用SQL Compare 通過使用所有默認(rèn)的Compare選項(xiàng),通過將它與空的目標(biāo)數(shù)據(jù)庫進(jìn)行比較,從原始Pubs數(shù)據(jù)庫的同一副本中生成構(gòu)建腳本。在Publishers表中,DEFAULTfor Country列的突然有了一個(gè)名稱:
[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表突然出現(xiàn)了名為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添加了它的約束,因此我們可以看到名稱發(fā)生了什么。
ALTER TABLE [dbo].[titles] ADD CONSTRAINT [FK__titles__pub_id__412EB0B6] FOREIGN KEY ([pub_id]) REFERENCES [dbo].[publishers] ([pub_id])
SQL Compare已使用系統(tǒng)生成的名稱將所有以系統(tǒng)命名的對(duì)象轉(zhuǎn)換為用戶命名的對(duì)象。通過將名稱應(yīng)用于以系統(tǒng)命名的對(duì)象,它實(shí)際上已經(jīng)錯(cuò)誤地表示了數(shù)據(jù)庫。它不必這樣做,但是它是默認(rèn)的“ Redgate”選項(xiàng)。
如果您總是通過簡單地更改現(xiàn)有對(duì)象來開發(fā)數(shù)據(jù)庫,那么這不太可能。但是,通過使用默認(rèn)選項(xiàng),您在隨后比較表以查看是否有任何更改時(shí)會(huì)遇到麻煩。您還限制了SQL Compare生成的任何腳本的值。我會(huì)舉例說明。相同的數(shù)據(jù)庫,相同的腳本,相同的服務(wù)器
讓我們嘗試另一個(gè)實(shí)驗(yàn)。這次,我們將在同一臺(tái)服務(wù)器上使用相同的原始Pubs構(gòu)建腳本創(chuàng)建Pubs數(shù)據(jù)庫的新副本。然后,我們使用SQL Compare比較兩個(gè)數(shù)據(jù)庫。令人驚訝的是,它發(fā)現(xiàn)它們是相同的,因?yàn)镾QL Server在執(zhí)行表DDL代碼時(shí)在兩個(gè)數(shù)據(jù)庫中使用相同的算法來生成系統(tǒng)名稱。
相同的數(shù)據(jù)庫,相同的腳本,不同的服務(wù)器
現(xiàn)在,我們?cè)诹硪粋€(gè)服務(wù)器上使用相同的原始Pubs構(gòu)建腳本創(chuàng)建一個(gè)相同的數(shù)據(jù)庫。這次一切都錯(cuò)了。我們要做的就是使用其他服務(wù)器。這兩個(gè)數(shù)據(jù)庫是相同的,因?yàn)樗鼈兪?由相同的腳本生成的,但是SQL Compare認(rèn)為表是不同的。
同一數(shù)據(jù)庫,不同腳本,同一服務(wù)器
好吧,當(dāng)然,我們可以在同一臺(tái)服務(wù)器上開發(fā),然后呢?我們可以通過另一個(gè)測試來證明這一點(diǎn)。如前所述,我們使用原始腳本構(gòu)建來構(gòu)建Pubs,使用SSMS從中生成構(gòu)建腳本,然后使用它來創(chuàng)建名為PubsTest的數(shù)據(jù)庫的新構(gòu)建。
最后,我們使用SQL Compare比較兩個(gè)數(shù)據(jù)庫Pubs和PubsTest:
允許SQL Compare將名稱賦予未命名約束的問題
如果將SQL Compare及其默認(rèn)選項(xiàng)用于檢查表差異,則將得到很多誤報(bào)。當(dāng)唯一的區(qū)別是約束的自動(dòng)系統(tǒng)名稱時(shí),表將被標(biāo)記為不同。如果兩個(gè)開發(fā)人員使用腳本工作以在開發(fā)它們時(shí)偶爾構(gòu)建表,即使它們實(shí)際上是相同的,它們也會(huì)在版本控制中被標(biāo)記為不同,因?yàn)檫@是基于字符串的比較,而不是語義比較。
SQL Compare將生成生成腳本,每次您刪除并重新創(chuàng)建系統(tǒng)命名的約束或從腳本重新創(chuàng)建表時(shí),這些生成腳本都會(huì)更改。如果您使用帶有默認(rèn)選項(xiàng)的SQL Compare,則版本控制系統(tǒng)可以輕松地阻塞微不足道的更改,并且敢于在對(duì)表進(jìn)行更改時(shí)重建表。
還有另一種尷尬。在將數(shù)據(jù)導(dǎo)入表中之前,您通常需要先禁用所有約束,然后重新啟用它們。您可以通過執(zhí)行以下操作禁用表上的所有約束:
ALTER TABLE MyTable NOCHECK CONSTRAINT ALL然后,您可以使用以下命令打開它們:
ALTER TABLE MyTable WITH CHECK CHECK CONSTRAINT ALL但是,通常,腳本通過按名稱分別標(biāo)識(shí)每個(gè)約束來禁用此功能,以禁用檢查,然后在數(shù)據(jù)導(dǎo)入后啟用檢查。有時(shí),它們會(huì)掉落并重新創(chuàng)建它們。在SQL中以外科手術(shù)精度單獨(dú)訪問約束的最簡單方法是按名稱引用。如果執(zhí)行此操作,則方便的數(shù)據(jù)導(dǎo)入例程將在數(shù)據(jù)庫的一個(gè)版本上創(chuàng)建的具有系統(tǒng)生成名稱的約束的名稱指定,該約束是在數(shù)據(jù)庫的一個(gè)版本上創(chuàng)建的,無法在同一服務(wù)器上創(chuàng)建的數(shù)據(jù)庫的精確副本上使用使用不同的腳本。
獲取SQL Compare以忽略系統(tǒng)命名的約束和索引名稱
有一個(gè)簡單的解決方案,可以在生成腳本時(shí)使Compare的行為與SSMS(和SMO)默認(rèn)行為保持一致。
默認(rèn)情況下,SQL Compare的選項(xiàng)“ 忽略系統(tǒng)命名的約束和索引名稱 ”為OFF。您應(yīng)該為您的SQL Compare副本啟用它并將其設(shè)置為默認(rèn)值。這樣做是為了強(qiáng)制SQL Compare認(rèn)識(shí)到系統(tǒng)命名的約束實(shí)際上沒有名稱,因此它必須使用其他方式比較它們。
如果使用sqlserver模塊從SSMS或SMO為對(duì)象或整個(gè)數(shù)據(jù)庫生成構(gòu)建腳本,則它將在腳本中正確表示系統(tǒng)命名的對(duì)象。如果打開SQL Compare的' 忽略系統(tǒng)命名的約束和索引名稱 '選項(xiàng),它將執(zhí)行相同的操作。您可能會(huì)想改為選擇“忽略約束和索引名稱”,但這不能解決此問題。
訪問個(gè)人約束
有人說,通過使用系統(tǒng)命名的約束,您使由單個(gè)約束引起的錯(cuò)誤難以理解。實(shí)際上,正如我在本文中所展示的那樣,系統(tǒng)生成的名稱是經(jīng)過專門設(shè)計(jì)的,以使其易于確定所涉及的表,約束類型甚至列。
也有人說這使得表格難以比較。當(dāng)然,這需要一種不同的方法。比較表時(shí),您需要根據(jù)其作用而不是其名稱來識(shí)別和比較以系統(tǒng)命名的約束。如果告訴您,SQL Compare很樂意這樣做。
DEFAULT約束很容易,因?yàn)橐涣兄荒苡幸粋€(gè)。如果默認(rèn)值更改為20到40,則它是一個(gè)不同的約束。甲CHECK上的列約束是由什么它確實(shí)是這樣,有效地,它執(zhí)行列和代碼是標(biāo)識(shí)符定義。表級(jí)CHECK約束由其代碼定義。一個(gè)FOREIGN KEY約束是引用表,引用的表和列的列表中標(biāo)識(shí)。
但是,實(shí)際上,很少有理由合理地動(dòng)態(tài)需求DELETE或ALTER約束。通常,您唯一需要訪問約束的時(shí)間就是打開或關(guān)閉約束。然后,您幾乎總是想一次全部關(guān)閉或打開它們。
結(jié)論
SQL中未命名約束的想法使一些開發(fā)人員和DBA感到震驚,但是如果使人們更容易使用約束,我全力以赴。實(shí)際上,我希望有更多的方法來鼓勵(lì)使用適當(dāng)?shù)募s束。
我總是在SQL Compare和SCA cmdlet中包含選項(xiàng)“ 忽略系統(tǒng)命名的約束和索引名稱 ”,這是我的默認(rèn)設(shè)置,因此在沒有此設(shè)置的情況下使用它時(shí)會(huì)感到震驚,因?yàn)殚_始發(fā)生奇怪的事情。通過包含它,您更有信心使用SQL Compare生成的對(duì)象級(jí)和數(shù)據(jù)庫級(jí)腳本將與SSMS生成的腳本具有相同的工作方式,但具有錯(cuò)誤檢查和回滾的好處錯(cuò)誤,并保存數(shù)據(jù)。
相關(guān)產(chǎn)品推薦:
SQL Prompt:SQL語法提示工具
SQL Toolbelt:Red Gate產(chǎn)品套包
SQL Monitor:SQL Server監(jiān)控工具
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請(qǐng)務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請(qǐng)郵件反饋至chenjj@fc6vip.cn
文章轉(zhuǎn)載自: