任何人都可以告诉我们最好的目标是什么:
要求:从VLT(近400 gb)中删除5列。
我们尝试这样做的那一刻,我们在生产方面面临空间问题,超时错误(通过SSMS)
我们试图插入任何临时表(通过保持身份关闭),但随后我们插入了所有近几十亿行数据,我们尝试切换身份,我们面临超时错误。
我们应该通过POWERSHELL进行这些操作,与在SSMS中进行相比会更好吗
限制:生产空间有限,tempdb因这些操作而快速增长。
请告知,从VLT中删除列的最佳方法是什么。
此致
答案 0 :(得分:13)
我会采用已经提到过的方法之一,但需要进行一些重要修改。假设您使用的是SQL Server 2008,请执行以下操作:
使用您想要保留的列制作现有超大型表的零长度副本:
select top 0 {{column subset}} into tbl_tableB from tableA
确保还将任何索引,约束等复制到新表中。标识列将由SELECT...INTO
语句适当处理。
重命名原始表格;我们将在下一步中用视图替换它。
exec sys.sp_rename @objname = 'tableA', @newname = 'tbl_tableA'
使用原始表名和UNION ALL
创建视图:
create view tableA
as
select {{column subset}} from tbl_tableA
union all
select {{column subset}} from tbl_tableB
这将保持与查询数据的应用程序的某种程度的兼容性。必须通过视图上的触发器处理INSERTs
,UPDATEs
和DELETEs
。 UNION ALL
将阻止tempdb中的压力,因为不会有排序(而不是直接UNION
),并且我们一次不会存在多行的副本。
使用DELETE
结合OUTPUT
子句从原始表中批量删除数据,同时将其插入新表中:
BEGIN TRAN
DELETE TOP (1000) /* or whatever batch size you want */
FROM
tbl_tableA
OUTPUT (
DELETED.{{column subset}} /* have to list each column here prefixed by DELETED. */
)
INTO
tbl_tableB (
{{column subset}} /* again list each column here */
)
/* Check for errors */
/* COMMIT or ROLLBACK */
/* rinse and repeat [n] times */
完成DELETEs
/ INSERTs
后,删除视图,删除原始表,重命名新表:
drop view tableA
drop table tbl_tableA
exec sys.sp_rename @objname = 'tbl_tableB', @newname = 'tableA'
这种方法的最重要的优点是DELETE
和INSERT
在同一事务中同时发生,这意味着数据将始终处于一致状态。您可以通过更改TOP
子句来增加批处理的大小,从而更好地控制事务日志的使用和阻塞。我已经在有和没有标识列的表上测试了这种精确的方法,并且效果很好。在一张非常大的桌子上,需要一段时间才能运行;可能是几个小时到几天,但它会完成所需的结果。
答案 1 :(得分:11)
ALTER TABLE ... DROP
本身是一个仅元数据的操作,只要能在表上获得独占锁,就会完成几乎即时的,这意味着使用该表的所有查询都必须排水(完成)。但是删除列并不会实际删除它们,请参阅SQL Server table columns under the hood。
如有必要,下一步是删除物理列。我在必要时调出 ',因为根据列类型,它可能不值得付出努力。对于可变长度列,您可以通过运行DBCC CLEANTABLE
来回收空间。但是,如果您在非压缩表上删除了固定大小的列(没有页面或行压缩),那么回收空间的唯一方法是重建表(堆或聚簇索引)。如果表已分区,您可以尝试一次重建脱机一个分区(ALTER TABLE ... REBUILD PARTITION = N
)。如果没有,只要您没有MAX类型列(此restriction is lifted in SQL Server 2012),您的最佳镜头就是在线重建。在线重建会生成大量日志(至少是数据大小的1.5倍),但它会在内部提交,因此日志备份维护可以回收空间,并且最终不会有600Gb的日志增长。如果在线重建不可行并且表没有分区,那么我将首先重新审视清除drop column的决定。
如果列清理是绝对必须并且您没有在线备选方案,那么您真的会面临一个痛苦的世界。做这么大的操作需要几天的准备,测试,并且完全不是微不足道的。您必须创建一个具有所需结构的新表,开始将数据批量传输并设置一些机制来跟踪对已经复制的数据所做的更改,然后将这些更改应用于副本。复制完所有数据后,应用自复制开始后发生的更改,您可以使用sp_rename
切换旧表和新表。总而言之,如果您可以使用现成的在线选项,那么您将更多更好。
答案 2 :(得分:2)
我会说另一个表和批处理作业的组合。
1 - 创建一个包含所需结构的新表。使用与旧表相同的聚簇索引键。
2 - 创建一个视图以合并新旧表,这样您就可以根据需要持续访问这两个表。要限制生产问题,可以将其命名为原始表,并将表重命名为_Old或其他内容。显然,只包括视图中所需的字段,而不是您要删除的字段。
3 - 在交易中:
JOIN
新表这具有日志增长率低(因为您正在批处理),数据库增长率低的优点(因为额外行的数量永远不会超过您的批处理大小),并且它是递增的,因此如果它太慢,您可以停止
BAD新闻是,你正在删除记录,所以一旦你开始你基本上都致力于这个过程。您可能还会从UNION
视图中获得tempdb压力,具体取决于需要进行多少排序。
答案 3 :(得分:0)
我或许会考虑使用您想要的模式创建一个新的分区表,并将数据插入到切换表中,然后将这些表切换到新表中。
如果您对分区表和索引不是很熟悉,我非常推荐Kimberly Tripp的这个excellent whitepaper。
将数据输入交换表时,可以通过执行以下操作强制执行最少的日志记录:
您需要使用跟踪标志610,如下所示:
DBCC TRACEON(610)
您需要使用表格上的tablock提示,如下所示:
INSERT newtable WITH (TABLOCK)
SELECT col1, col2, col3, col4
FROM oldtable
WHERE col1 BETWEEN min and max
交换表需要具有聚簇索引
答案 4 :(得分:0)
不能说我有使用大小的表的经验,但如果是我并且正在寻找尝试的东西,我会尝试BCP将数据(只有你要保留的列)输出到O / S文件,删除表,然后将数据BCP回到一个只有我需要的列的新表。当然,这假设您可以在执行此维护时使服务器脱机(并且在开始之前您有良好的备份)。