前言
我今天正在修改 SQL Server 2008 中的一列,将数据类型从货币(18,0)更改为(19,2)。
我从 SQL Server 收到错误“您所做的更改需要删除并重新创建以下表”。
在您争先恐后地回答之前,请阅读以下内容:
我已经知道工具 ► 选项 ► 设计器 ► 表和数据库设计器 ► 取消选中“防止保存需要重新创建表的更改”框。 ......所以不要回答!
实际问题
我的实际问题是其他问题,如下所示:
这样做有什么负面影响/可能的缺点吗?
取消选中此框时,表实际上是否会自动删除并重新创建?
如果是这样,该表是否是源表的 100% 精确副本?
工具 --> 选项 --> Designers 节点 --> 取消选中“防止保存需要重新创建表的更改”。
只有在 SQL Server 的 Management Studio 被编程为知道如何执行此操作的唯一方法时,才会删除并重新创建该表。
在某些情况下,它会在不需要时执行此操作,但也存在您在 Management Studio 中所做的编辑不会删除并重新创建的情况,因为它不必这样做。
问题是枚举所有案例并确定它们落在哪一边将是相当乏味的。
这就是为什么我喜欢在查询窗口中使用 ALTER TABLE
,而不是隐藏他们正在做的事情的视觉设计师(坦率地说有错误) - 我确切地知道会发生什么,并且我可以为以下情况做好准备唯一的可能性是删除并重新创建表(这比 SSMS 对您执行此操作的频率要少一些)。
这样做有什么负面影响/可能的缺点吗?
当然。如果您可以自己编写更改脚本而无需重建整个表,那就更好了 - 考虑表为 10TB 并且数据库记录大量日志(想想同步 AG、更改跟踪、复制、编写不佳的触发器)和表的情况被高度访问-这是灾难的潜在秘诀。如果您的更改是您可以应用在线提示或添加列并批量复制数据而不是 GUI 将执行的全部或全部操作,那么这会更好。
取消选中此框时,表实际上是否会自动删除并重新创建?
它可能。有一系列方案,结果取决于 SSMS 的版本、SQL Server 的版本,有时甚至是版本。您可以通过选中该框并尝试首先将更改应用于数据库的无意义副本来进行检查,但是恕我直言,使用实际的 ALTER TABLE 脚本而不是尖尖的点击 GUI 是一种方法。
如果是这样,该表是否是源表的 100% 精确副本?
是的,如果 SSMS 必须重建表,它在完成后将是一个 100% 精确的副本(当然更改除外),但这可能是下周三。该过程创建表的新版本,将所有数据复制到其中,然后删除旧表并重命名新表。
Reference - 关闭此选项可以帮助您避免重新创建表,它也可能导致更改丢失。例如,假设您启用 SQL Server 2008 中的更改跟踪功能来跟踪对表的更改。当您执行导致重新创建表的操作时,您会收到“症状”部分中提到的错误消息。但是,如果关闭此选项,则在重新创建表时会删除现有的更改跟踪信息。因此,Microsoft 建议您不要通过关闭该选项来解决此问题。
只有在以下情况下,SQL Server 才会删除并重新创建表:
添加新列
更改列的 Allow Nulls 设置
更改表格中的列顺序
更改列数据类型
使用 ALTER 更安全,因为如果在您重新创建表时元数据丢失,您的数据将会丢失。
IDENTITY
属性。
是的,这有负面影响:
如果您编写出被此标志阻止的更改,您会得到类似下面的脚本(我只是将 Contact 中的 ID 列转换为自动编号的 IDENTITY 列,但该表具有依赖关系)。请注意以下运行时可能发生的潜在错误:
甚至微软也警告说这可能会导致数据丢失(该评论是自动生成的)!在一段时间内,不强制执行外键。如果您在 ssms 中手动运行它并且 ' EXEC('INSERT INTO ' 失败,并且您让以下语句运行(默认情况下它们会运行,因为它们被 'go' 拆分),那么您将插入 0 行,然后删除旧表。如果这是一张大表,插入的运行时间可能会很大,并且事务持有模式修改锁,因此会阻塞很多东西。
--
/* 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
GO
ALTER TABLE raw.Contact
DROP CONSTRAINT fk_Contact_AddressType
GO
ALTER TABLE ref.ContactpointType SET (LOCK_ESCALATION = TABLE)
GO
COMMIT
BEGIN TRANSACTION
GO
ALTER TABLE raw.Contact
DROP CONSTRAINT fk_contact_profile
GO
ALTER TABLE raw.Profile SET (LOCK_ESCALATION = TABLE)
GO
COMMIT
BEGIN TRANSACTION
GO
CREATE TABLE raw.Tmp_Contact
(
ContactID int NOT NULL IDENTITY (1, 1),
ProfileID int NOT NULL,
AddressType char(2) NOT NULL,
ContactText varchar(250) NULL
) ON [PRIMARY]
GO
ALTER TABLE raw.Tmp_Contact SET (LOCK_ESCALATION = TABLE)
GO
SET IDENTITY_INSERT raw.Tmp_Contact ON
GO
IF EXISTS(SELECT * FROM raw.Contact)
EXEC('INSERT INTO raw.Tmp_Contact (ContactID, ProfileID, AddressType, ContactText)
SELECT ContactID, ProfileID, AddressType, ContactText FROM raw.Contact WITH (HOLDLOCK TABLOCKX)')
GO
SET IDENTITY_INSERT raw.Tmp_Contact OFF
GO
ALTER TABLE raw.PostalAddress
DROP CONSTRAINT fk_AddressProfile
GO
ALTER TABLE raw.MarketingFlag
DROP CONSTRAINT fk_marketingflag_contact
GO
ALTER TABLE raw.Phones
DROP CONSTRAINT fk_phones_contact
GO
DROP TABLE raw.Contact
GO
EXECUTE sp_rename N'raw.Tmp_Contact', N'Contact', 'OBJECT'
GO
ALTER TABLE raw.Contact ADD CONSTRAINT
Idx_Contact_1 PRIMARY KEY CLUSTERED
(
ProfileID,
ContactID
)
GO
ALTER TABLE raw.Contact ADD CONSTRAINT
Idx_Contact UNIQUE NONCLUSTERED
(
ProfileID,
ContactID
)
GO
CREATE NONCLUSTERED INDEX idx_Contact_0 ON raw.Contact
(
AddressType
)
GO
ALTER TABLE raw.Contact ADD CONSTRAINT
fk_contact_profile FOREIGN KEY
(
ProfileID
) REFERENCES raw.Profile
(
ProfileID
) ON UPDATE NO ACTION
ON DELETE NO ACTION
GO
ALTER TABLE raw.Contact ADD CONSTRAINT
fk_Contact_AddressType FOREIGN KEY
(
AddressType
) REFERENCES ref.ContactpointType
(
ContactPointTypeCode
) ON UPDATE NO ACTION
ON DELETE NO ACTION
GO
COMMIT
BEGIN TRANSACTION
GO
ALTER TABLE raw.Phones ADD CONSTRAINT
fk_phones_contact FOREIGN KEY
(
ProfileID,
PhoneID
) REFERENCES raw.Contact
(
ProfileID,
ContactID
) ON UPDATE NO ACTION
ON DELETE NO ACTION
GO
ALTER TABLE raw.Phones SET (LOCK_ESCALATION = TABLE)
GO
COMMIT
BEGIN TRANSACTION
GO
ALTER TABLE raw.MarketingFlag ADD CONSTRAINT
fk_marketingflag_contact FOREIGN KEY
(
ProfileID,
ContactID
) REFERENCES raw.Contact
(
ProfileID,
ContactID
) ON UPDATE NO ACTION
ON DELETE NO ACTION
GO
ALTER TABLE raw.MarketingFlag SET (LOCK_ESCALATION = TABLE)
GO
COMMIT
BEGIN TRANSACTION
GO
ALTER TABLE raw.PostalAddress ADD CONSTRAINT
fk_AddressProfile FOREIGN KEY
(
ProfileID,
AddressID
) REFERENCES raw.Contact
(
ProfileID,
ContactID
) ON UPDATE NO ACTION
ON DELETE NO ACTION
GO
ALTER TABLE raw.PostalAddress SET (LOCK_ESCALATION = TABLE)
GO
COMMIT
不定期副业成功案例分享