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

时间:2014-11-24 21:33:07

标签: 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,但这要求目标表的数据类型与源表匹配,这就是我要解决的问题!

有什么建议吗?我看错了吗?

2 个答案:

答案 0 :(得分:3)

首先,谢谢你这样做。这是一个明显的胜利,许多人看不到很多价值,但这将是值得的:)。让世界变得更加健康。

关于IsActive是一个布尔值。我的猜测是你想把它变成BIT字段。这可能是要走的路,但有时最好与TINYINT一起使用,因为有可能将意义扩展到2个以上的状态。在这种情况下,它真的变得更StatusID。通常,这是一种简单的开始,如活跃 / 非活动,但稍后可能已删除和/或其他。从大小调整的角度来看,TINYINT总是1个字节。另一方面,BIT为1个字节,最多可包含8个BIT字段。意思是,一个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!(需要免费注册)。为了防止该文章出现问题,以下是基本步骤:

  1. 创建一个具有理想结构的新表 - [tableNew]。如果您使用的是Enterprise Edition,请考虑启用ROW或PAGE压缩,因为它们有时可以提供帮助。但请先做一些研究,因为有些情况会产生负面影响。有关MSDN的文档可以帮助您解决这个问题以及一些有助于估算潜在节省的工具。但即使您启用了压缩功能,我也不会将此操作视为替换您在此处执行的项目。
  2. 在[table]上添加触发器AFTER UPDATE, DELETE以保持同步更改(但无需担心新行)
  3. 创建一个SQL代理作业,批量移动缺失的行。在执行INSERT INTO [tableNew] (Columns) SELECT TOP (n) Columns FROM [table] WHERE ?? ORDER BY ??
  4. 的循环中执行此操作
  5. WHERE和ORDER BY子句取决于具体情况。它们应该适合充分利用聚集索引。如果新表的聚集索引在结构上与旧/当前表相同,那么在每个循环的开始处,您可以从[tableNew]获取MAX([id])并使用它来获取WHERE table.[id] > @MaxIdInTableNew ORDER BY table.[id]
  6. 在您需要进行完全切换之前一周左右,创建新表,触发当前表和SQL代理作业。这个时间框架可能会根据您的情况而改变,但只要确保给自己充足的时间。对于完成迁移行的工作要好得多,并且一次只能进行一些滴流,而不是因为释放本应该开始时完全设置为100k shy。
  7. 如果计划要迁移其他相关表格(您希望转换为INT的两个FK的PK参考),那么现在在INT处填写这些字段,并且只需要#&# 39; t添加FK,直到将其他表迁移到将INT字段作为其PK。您不希望再次重建此表只是为了对FK字段进行更改。
  8. 在切换期间(当然是在TRY / CATCH中):
    1. BEGIN TRAN
    2. 对两个表执行最后的行计数以确保所有内容都被移动(可能希望在发布之前检查行的完整性,以确保触发器按预期执行更新和删除)。
    3. 将当前表重命名为" old"
    4. 重命名" new"表没有" new"
    5. 删除SQL代理作业(或至少禁用它)
    6. 重命名和依赖对象,例如约束等
    7. COMMIT

答案 1 :(得分:1)

不要放弃

创建一个包含正确列类型的表,然后插入循环

或者您可以一次完成一列。

Update table 
set Number_1tiny = Number_1 
where Number_1 is not null 
and Number_1tiny <> Number_1