問題:
SQL Server有一個不錯的常用特性,就是標識列。利用這個特性,你可以輕松地在表里為每一行創建唯一的值。添加一個新的列并設置為標識列,或者刪除現有標識列都很簡單,但是如果是要把一個現有的列改為標識列,或者去掉現有的標識列的標識屬性,那應該怎么做呢?
專家解答:
要做到這一步并不容易,或者說沒有什么簡單易行的方法。SQL Server的設計里并沒有很簡單的就現有的列加上或去掉標識屬性的方法。最安全的辦法是,創建一個新的列并設置為標識列,或者創建一個新的表,然后把數據移植進去。
我們來看看一些例子:
例一
現有一個簡單的表,表里含有兩列,其中一列為標識列。
CREATE TABLE [dbo].[Test1](
[id] [int] IDENTITY(1,1) NOT NULL,
[name] [nchar](10) NULL
)
如果我們用SQL Server Management Studio來去掉”id”列的標識值,這樣就會創建出一個新的臨時表,原表的數據都移到了臨時表中,然后刪除原表并為新表重命名。我們可以從下面的腳本里看到這個過程。
要看到這個腳本,只要使用 Management Studio來進行改動,然后右擊designer,選擇"Generate Change Script"。
以下是引用片段:
/* To prevent any potential data loss issues, you should review this
script in detail before running it outside the context of the database designer.*/
BEGIN TRANSACTION
SET QUOTED_IDENTIFIER ON
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
SET CONCAT_NULL_YIELDS_NULL ON
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
COMMIT
BEGIN TRANSACTION
GO
CREATE TABLE dbo.Tmp_Test1
(
id INT NOT NULL,
name NCHAR(10) NULL
) ON [PRIMARY]
GO
IF EXISTS(SELECT * FROM dbo.Test1)
EXEC('INSERT INTO dbo.Tmp_Test1 (id, name)
SELECT id, name FROM dbo.Test1 WITH (HOLDLOCK TABLOCKX)')
GO
DROP TABLE dbo.Test1
GO
EXECUTE sp_rename N'dbo.Tmp_Test1', N'Test1', 'OBJECT'
GO
COMMIT
例二
我們現在來把它弄得更復雜一點,在表里設置主鍵,然后創建以外鍵約束的另一個表,以第一個表主鍵為索引。我們可以看到這樣做需要花更多工夫。
以下是引用片段:
CREATE TABLE [dbo].[Test1](
[id] [int] IDENTITY(1,1) NOT NULL,
[name] [nchar](10) NULL,
CONSTRAINT [PK_Test1] PRIMARY KEY CLUSTERED
(
[id] ASC
))
GO
CREATE TABLE [dbo].[Test2](
[id] [int] NULL,
[name2] [nchar](10) NULL
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Test2] WITH CHECK ADD CONSTRAINT [FK_Test2_Test1] FOREIGN KEY([id])
REFERENCES [dbo].[Test1] ([id])
GO
ALTER TABLE [dbo].[Test2] CHECK CONSTRAINT [FK_Test2_Test1]
GO
如果我們按照上述的方法,然后使用Management Studio去掉表“test 1”中“id”列的標識值,可以從腳本里看到,這樣需要更多的步驟才能達到我們的目的。
首先創建一個列屬性正確的臨時表“Tmp_Test1”
把數據從表“Test1”移植到臨時表“Tmp_Test1”中
去掉Test2的外鍵約束
刪除表“Test1”
臨時表“Tmp_Test1”重命名為“Test1”
在表“Test1”里創建主鍵
最后重新在表“Test2”里創建外鍵約束。步驟太多了!
以下是引用片段:
/* To prevent any potential data loss issues, you should review this
script in detail before running it outside the context of the database designer.*/
BEGIN TRANSACTION
SET QUOTED_IDENTIFIER ON
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
SET CONCAT_NULL_YIELDS_NULL ON
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
COMMIT
BEGIN TRANSACTION
GO
CREATE TABLE dbo.Tmp_Test1
(
id INT NOT NULL,
name NCHAR(10) NULL
) ON [PRIMARY]
GO
IF EXISTS(SELECT * FROM dbo.Test1)
EXEC('INSERT INTO dbo.Tmp_Test1 (id, name)
SELECT id, name FROM dbo.Test1 WITH (HOLDLOCK TABLOCKX)')
GO
ALTER TABLE dbo.Test2
DROP CONSTRAINT FK_Test2_Test1
GO
DROP TABLE dbo.Test1
GO
EXECUTE sp_rename N'dbo.Tmp_Test1', N'Test1', 'OBJECT'
GO
ALTER TABLE dbo.Test1 ADD CONSTRAINT
PK_Test1 PRIMARY KEY CLUSTERED
(
id
) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
COMMIT
BEGIN TRANSACTION
GO
ALTER TABLE dbo.Test2 ADD CONSTRAINT
FK_Test2_Test1 FOREIGN KEY
(
id
) REFERENCES dbo.Test1
(
id
) ON UPDATE NO ACTION
ON DELETE NO ACTION
GO
COMMIT
同樣,如果我們想要對現有的列進行修改,把其中一列變成標識列的話,需要花費同樣的工夫。
另外一種可行的方法就是增加一列并設置為標識列,或者增加一列然后把舊列的數據移植到新列里。然后你可以刪除之前的列,使用sp_rename存儲過程來對進行新增列重命名。如果這些列里含有索引、外鍵或其他約束,你需要把它們都去掉才能作上述的修改。所以,這個方法也快不了多少。
我們現在知道,要修改現有列的標識屬性沒有什么捷徑可走。你也可以在網絡上搜索到其他的辦法,可以修改系統表里的值。這些方法確實可以行得通,不過要注意的是,一旦出了什么差錯,你可能會把數據全毀掉。
對于比較小的數據庫系統而言,這里介紹的方法沒有什么太大問題。不過對于忙碌的大數據庫系統來說,去掉表里的約束、修改表里的標識屬性就會是一件讓人頭疼的事情了?,F在只能是期待SQL 2008里會不會出現什么讓人眼前一亮的新特性了。