我的一张桌子有超过1.5亿条记录。因此,当前的类型为:
id (Primary Key, Bigint)
idResult (Foreign key, Bigint null)
idItem (Foreign Key, Bigint null)
Number_1 (Bigint null)
Number_2 (Bigint null)
IsActive (Bigint null)
Number_1和Number_2不能大于10。IsActive显然是布尔值。这些列中的任何一个都不能在代码库中的其他任何地方为空。我也想将外键字段更改为int,但这又是另一个故事。该表是在我开始工作的几年前建立的,我们正在经历一些成长的烦恼。
我正在寻找转换这些列(以及其他表上的其他几个表,虽然这是主要的冒犯者)并回收该磁盘空间的最佳方法。我已经尝试了一个直接的
Alter Table
,但是,按预期,这是行不通的。我不记得具体的错误,但我相信它与所讨论的表的大小有关。现在,我正在考虑手动删除并重新创建表,但是我正在努力寻找一种更好的方法,除了使用
Select TOP 10000 * FROM dbo.TABLENAME WHERE id > 0
并简单地将where子句递增几次。我已经看过
Switch To
,但这要求目标表的数据类型与源表匹配,这就是我要解决的问题!有什么建议?我看错了方向吗?
最佳答案
首先,谢谢您这样做。如此明显的胜利使许多人看不到太多的价值,但这将是值得的:)。使世界变得更加微妙。
关于IsActive
是布尔值。我的猜测是您正在考虑将其设置为BIT
字段。那可能是要走的路,但是有时最好使用TINYINT
,因为有可能将含义扩展到两个以上的状态。在这种情况下,它实际上成为StatusID
的更多内容。通常情况是,某些事物以“活动/不活动”的形式简单地开始,但后来可能是“已删除”和/或其他。从大小的角度来看,TINYINT
始终为1个字节。另一方面,对于最多8个BIT
字段,BIT
是1个字节。意思是,一个BIT
字段是1个字节,2个BIT
字段也是一个字节,依此类推,单个字节中最多可存储8个BIT
字段。因此,当表只有1个BIT
字段时,选择TINYINT
而不是BIT
不会节省空间。只是要考虑的事情。
如您所见,对于大型表来说,执行ALTER TABLE有点麻烦。一种选择(虽然不是一个很好的选择)是添加一个带有NOT NULL
值的Number_1new
字段(DEFAULT
)(由于默认值,这将是瞬时的,至少从SQL 2012开始,这是默认的),它们自然会拥有(例如255),然后在循环中缓慢迁移值,如下所示:
UPDATE TOP (5000) tab
SET tab.Number_1new = tab.Number_1
FROM [table] tab
WHERE tab.Number_1new = 255;
当完成后,请执行以下操作:
sp_rename 'table.Number_1', 'Number_1old', 'COLUMN';
sp_rename 'table.Number_1new', 'Number_1', 'COLUMN';
当然,最好将其包装在TRANSACTION中,并将其包装在TRY / CATCH中。当相关代码已更新且所有内容均已测试并且数据看起来不错时,可以删除
Number_1old
列。但是,我发现的最好方法是创建一个新表,慢慢过渡数据,然后同时交换表和代码。我在SQL Server Central上的文章中详细介绍了步骤:Restructure 100 Million Row (or more) Tables in Seconds. SRSLY!(需要免费注册)。万一遇到有关该文章的问题,请执行以下基本步骤:
创建具有理想结构的新表-[tableNew]。如果您使用的是Enterprise Edition,请考虑启用ROW或PAGE压缩,因为它们有时会有所帮助。但是,请先做一些研究,因为在某些情况下它们会产生负面影响。 MSDN上有文档可帮助您解决问题,还有一些工具可帮助您估计潜在的节省。但是,即使您确实启用了压缩功能,我也不会认为该操作会取代您在此处执行的项目。
在[表]上添加触发器
AFTER UPDATE, DELETE
以保持更改同步(但无需担心新行)创建一个SQL Agent Job,将其移至丢失的行中。在执行
INSERT INTO [tableNew] (Columns) SELECT TOP (n) Columns FROM [table] WHERE ?? ORDER BY ??
的循环中执行此操作WHERE和ORDER BY子句取决于情况。它们应适合于充分利用聚集索引。如果新表的聚集索引在结构上与旧表/当前表相同,则在每个循环开始时,您可以从[tableNew]获取MAX([id])并使用它来获取
WHERE table.[id] > @MaxIdInTableNew ORDER BY table.[id]
。在需要进行完全转换之前,一周左右创建一个新表,在当前表上触发,并执行SQL Agent Job。时间范围可能会根据您的情况而改变,但是请确保给自己足够的时间。对于这项工作而言,最好完成一次迁移行,并且一次只进行少量迁移,而不是像整个发行版那样要落后整个版本10万。
如果计划迁移其他相关表(要转换为
INT
的两个FK的PK引用),则现在在此处将这些字段设置为INT
,只是不要添加FK,直到其他表被迁移到以INT字段作为其PK。您不想只为了对FK字段进行更改而不必再次重建该表。在转换期间(当然,在TRY / CATCH中):
开始TRAN
对两个表进行最后的行计数,以确保所有内容都移到了上面(可能要在发行前检查行,以确保触发器按预期进行了更新和删除)
将当前表重命名为“旧”
重命名“新”表以不具有“新”
删除SQL Agent作业(或至少禁用它)
重命名和依赖对象,例如约束等
承诺
关于sql-server - 缩小非常大的表上的数据类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27114565/