sql-server - 缩小非常大的表上的数据类型

标签 sql-server tsql sql-server-2012

我的一张桌子有超过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/

相关文章:

tsql - 使用 SQL INSERT 创建带有 FileTable 的目录

sql - 基于连续值的累计和

sql-server - SQL 查询以获取最后 4 个季度和年份的最后 2 位数字

sql - t-sql 中有循环吗?使用它们是个好主意吗?

sql - 选择具有多个 DATETIME 值的最大日期

tsql - T-SQL : Parsing String with Multiple delimiters

sql - 从纬度和经度字段创建几何/地理字段 (SQL Server)

sql-server - 查询按最后四位数字对数据网格列进行排序?

sql-server - CTE SQL查询中的多个递归联合所有选择

c# - Principal Role 的多重性必须为 0..1