我们有一张桌子:
`CREATE TABLE dbo.Account (
AccountID INT NOT NULL PRIMARY KEY IDENTITY,
AccountName NVARCHAR(100) NOT NULL);
我想添加一个PartnerAccountKey
列并为其添加一个唯一约束(在我假设的部署后脚本中填充数据)
CREATE TABLE dbo.Account (
AccountID INT NOT NULL PRIMARY KEY IDENTITY,
AccountName NVARCHAR(100) NOT NULL,
PartnerAccountKey INT NOT NULL,
CONSTRAINT UK_Account_PartnerKey UNIQUE (PartnerAccountKey));
问题是在我的部署后脚本运行之前创建了唯一约束,因此它出错。我没有看到如何在创建列和创建唯一约束之间填充数据。
答案 0 :(得分:6)
我也发现了这方面的问题,经过几个小时尝试不同的方法后,我提出了以下两种方法。
这些是变通方法,可能并不理想,但希望它们有所帮助。
1)需要SQL2008或更高版本。
基本上,这利用了直到部署后脚本之后才应用CHECK CONSTRAINTS这一事实。
因此,我创建了一个过滤的唯一索引,该索引排除了默认值,然后添加了一个检查约束以防止使用默认值。
但是,您需要选择一个您知道不会在生产中使用的值。对于这个例子,我假设我永远不会有一个PartnerAccountKey值为零。但是,您可以通过在表定义中创建默认值来选择任何数字。
我的发布选项包括"生成智能默认设置(如果适用)"和#34;新约束的脚本验证"
我更改了您的表定义,如下所示。
CREATE TABLE [dbo].[Account]
(
AccountID INT NOT NULL PRIMARY KEY IDENTITY(1,1),
AccountName NVARCHAR(100) NOT NULL,
PartnerAccountKey INT NOT NULL
);
GO
ALTER TABLE dbo.Account
ADD CONSTRAINT CK_PartnerAccountKey CHECK (PartnerAccountKey!=0);
GO
CREATE UNIQUE NONCLUSTERED INDEX [IX_Acocunt_PartnerKey]
ON dbo.[Account] (PartnerAccountKey) WHERE PartnerAccountKey!=0;
GO
发布脚本运行时
在您的帖子部署脚本中,您可以填充正确的值
IF EXISTS (SELECT 1 FROM dbo.Account WHERE PartnerAccountKey=0)
BEGIN
-- Migrate Data Here - Example
UPDATE
dbo.Account
SET
PartnerAccountKey = AccountID;
END;
以下是发布脚本中的重要部分。
ALTER TABLE [dbo].[Account]
ADD [PartnerAccountKey] INT NOT NULL,
CONSTRAINT [SD_Account_cb8fbf98ba884b92b63b4b2017b7de20]
DEFAULT 0 FOR [PartnerAccountKey];
....
ALTER TABLE [dbo].[Account]
DROP CONSTRAINT [SD_Account_cb8fbf98ba884b92b63b4b2017b7de20];
....
CREATE UNIQUE NONCLUSTERED INDEX [IX_Acocunt_PartnerKey]
ON [dbo].[Account]([PartnerAccountKey] ASC)
WHERE PartnerAccountKey!=0;
....
ALTER TABLE [dbo].[Account] WITH NOCHECK
ADD CONSTRAINT [CK_PartnerAccountKey]
CHECK (PartnerAccountKey!=0);
....
/*
Post-Deployment Script Template
*/
IF EXISTS (SELECT 1 FROM dbo.Account WHERE PartnerAccountKey=0)
BEGIN
-- Migrate Data Here
UPDATE
dbo.Account
SET
PartnerAccountKey = AccountID;
END;
/*
End - Post Deploy
*/
....
ALTER TABLE [dbo].[Account]
WITH CHECK CHECK CONSTRAINT [CK_PartnerAccountKey];
2)需要SQL2012或更高版本。
这种方法可能更干净,但您可能需要在某些时候重新访问脚本以进行清理。
它使用序列来确保新创建的列具有唯一值。
如果您选择删除默认和/或序列,则下次发布时将再次创建。这不是一个真正的问题,因为部署后的脚本只会再次删除它们。
首先创建序列。我决定从-1开始向后计数,这样我就可以确定这些不是真正的价值。
CREATE SEQUENCE [dbo].[TempSequence]
AS INT
START WITH -1
INCREMENT BY -1
NO MAXVALUE
NO CYCLE
CACHE 10;
接下来是表格脚本。
CREATE TABLE [dbo].[Account]
(
AccountID INT NOT NULL PRIMARY KEY IDENTITY(1,1),
AccountName VARCHAR(20) NOT NULL,
PartnerAccountKey INT NOT NULL CONSTRAINT [DF_PartnerAccountKey]
DEFAULT (NEXT VALUE FOR TempSequence),
CONSTRAINT [UK_Account_PartnerKey] UNIQUE (PartnerAccountKey)
);
后部署脚本。
IF EXISTS (SELECT 1 FROM dbo.Account WHERE PartnerAccountKey < 0)
BEGIN
-- Example - Migrate Data
UPDATE
dbo.Account
SET
PartnerAccountKey = AccountID;
END;
GO
-- Optional Remove the default
IF EXISTS (SELECT 1 FROM sys.default_constraints WHERE
name = 'DF_PartnerAccountKey' AND object_id=OBJECT_ID(N'dbo.Account'))
BEGIN
ALTER TABLE dbo.Account DROP CONSTRAINT [DF_PartnerAccountKey];
END;
GO
-- Optional Remove the sequence
IF EXISTS (SELECT 1 FROM sys.sequences WHERE name = 'TestSequence')
BEGIN
DROP SEQUENCE TestSequence;
END;
GO
以下是生成的发布脚本的相关部分。
CREATE SEQUENCE [dbo].[TempSequence]
AS INT
START WITH -1
INCREMENT BY -1
CACHE 10;
....
ALTER TABLE [dbo].[Account]
ADD [PartnerAccountKey] INT CONSTRAINT [DF_PartnerAccountKey]
DEFAULT ( NEXT VALUE FOR TempSequence) NOT NULL;
....
ALTER TABLE [dbo].[Account]
ADD CONSTRAINT [UK_Account_PartnerKey] UNIQUE NONCLUSTERED
([PartnerAccountKey] ASC);
....
/*
Post-Deployment Script Template
*/
IF EXISTS (SELECT 1 FROM dbo.Account WHERE PartnerAccountKey < 0)
BEGIN
-- Example - Migrate Data
UPDATE
dbo.Account
SET
PartnerAccountKey= AccountID;
END;
-- Optional Remove the default
IF EXISTS (SELECT 1 FROM sys.default_constraints WHERE
name = 'DF_PartnerAccountKey' AND object_id=OBJECT_ID(N'dbo.Account'))
BEGIN
ALTER TABLE dbo.Account DROP CONSTRAINT [DF_PartnerAccountKey];
END;
-- Optional Remove the sequence
IF EXISTS (SELECT 1 FROM sys.sequences WHERE name = 'TestSequence')
BEGIN
DROP SEQUENCE TestSequence;
END;
我在SQL2012 Developer Edition,Visual Studio 2013 Premium和SSDT 12.0.40403.0上测试了这两种方法。
答案 1 :(得分:1)
最好的方法是在两个版本中执行此操作 - 一个用于添加列,另一个用于添加约束 - 或者将Unique约束暂时放入Post-Deploy脚本中,然后在发布后将其添加回项目。 (或在创建之前检查是否存在)
我发现在尝试创建设置了ONLINE = ON或MAXDOP选项的索引时,我必须做类似的事情。 SSDT在创建索引时会忽略这些选项,但我可以在部署后的脚本中创建它们,然后将它们移动到项目的主要部分。