在批量数据上执行时,使用ALTER提高列的精度是否会影响数据库性能?

时间:2014-09-22 12:34:19

标签: sql sql-server sql-server-2008 alter-table

我在一个实时数据库中有一个SQL表,它有超过600万行,我想提高特定列的精度:

ALTER TABLE sales ALTER COLUMN amount DECIMAL(8,4)

现在我的问题是,如果我执行上面的查询,SQL Server会重新计算每个单元格并将新值写回同一个单元格吗? (如果是,那意味着它肯定需要很长时间才能执行,而我们在数据库上的其他活动也会受到影响。)或者,该语句是否会以其他方式执行?

2 个答案:

答案 0 :(得分:6)

快速更正

decimal(8, 2)转换为decimal(8, 4)并不能实际提高精度;它增加了比例

来自decimal/numeric - TSQL

  • 精度 小数点左侧和右侧可以存储的最大小数位数
  • scale 小数点右侧可以存储的最大小数位数。比例必须是从0到p的值。

所以总的位数(以及存储要求)没有改​​变。

答案

如果你有超过 9999.9999 的任何值,你将会以算术溢出错误的形式出现不好的时间。要容纳所有可能的(8,2)值,您需要将列增加到decimal(10, 4)

但是,这会增加列的存储要求,从5到9个字节。因此,就可用性和事务日志的影响而言,这相当于update语句。

我根据测试发现,至少对于SQL Server 2008R2,如果存储要求发生变化,增加的精度只会导致数据更新。基本上如果列的新精度值与前一个相同的存储(或更小,并且没有发生截断)大小类别,则表格数据不受影响

对于600万行表,对事务日志的影响大约为2.5GB。它不一定会增长到这个数量,但它会消耗多少空间。我的测试使用了大约200万行,alter语句导致日志从1MB增长到~850MB。

至于对性能的影响(需要多长时间),在不知道任何有关服务器硬件的情况下加载它是不可能的。如果您非常担心要避免修改表,那么最好的方法可能就是表交换:

使用所需的架构创建一个新表(sales_tmp),然后复制数据:

insert sales_tmp
select * from sales;

如果您可以确保在操作期间不会修改sales表格,则不必担心会因事务和锁定而阻止它。否则,repeatable read事务就足够了,并且在操作期间至少不会阻止对sales表的读取。

然后:

  1. 对新表的复制权限
  2. 重新创建索引&外键引用
  3. 放弃旧桌子
  4. 重新命名(sales_tmpsales
  5. 如果您在受影响的表上设置了复制或其他奇特的东西,则可能会出现一些问题。遗憾的是,禁用和重新启用这些并不是一件容易的事。

    交易日志

    如果您担心更新600万条记录对交易日志的影响,您需要批量更新记录。其大小将根据您的需要而有所不同。我会推荐1,000-10,000。

    IMO,我不认为你真的需要担心这个问题,除非你的数据库服务器真的受到空闲空间的限制,但这些信息可能对未来有用。

    如果您的恢复模型是SIMPLE,那么日志包含也是如此。如果它是完全的,那就更难了。

    尽管Blam在他的回答中声称,他的循环实现绝对不保证任何包含事务日志文件。

    SIMIPLE recovery

    在循环体的末尾发出checkpoint语句,以确保刷新日志数据。无论如何,SQL Server会定期执行此操作,在大多数情况下,您不会注意到,但在某些情况下,您将。下面是高度简化的psudeocode:

    while @rows_left > 0
    begin
        -- update/copy rows
        checkpoint;
    end
    

    完全恢复

    这比较棘手,因为日志会无限增长,直到您进行日志备份,这会将日志页面标记为非活动状态,并使SQL Server能够重新使用已为日志文件分配的空间。除了推荐一些资源之外,我不会详细讨论这个问题:

答案 1 :(得分:2)

我没有得到与Matt相同的结论/发现

decimal and numeric (Transact-SQL)

  

对于十进制和数字数据类型,SQL Server会考虑每个   精度和比例的特定组合作为不同的数据类型。   例如,十进制(5,5)和十进制(5,0)被认为是不同的   数据类型。

(8,4)是与(8,2)不同的类型,必须施放。

你并没有提高精确度。相同的精度 - 不同的规模。

decimal(8,2)12356.78不会转换为十进制(8,2) 我测试了包含该值的小数(8,2)列,SSMS不允许我将其更改为十进制(8,4)

您将需要转到十进制(10,4)并且它将转换/转换

在集合@ dec84 = @ dec82;

上失败
declare @dec82 decimal(8,2);
declare @dec84 decimal(8,4);
set @dec82 = 123456.78;
set @dec84 = 1234.5678;
print @dec82;
print @dec84;
set @dec84 = @dec82;
print @dec84;

以适当的精度向现有表添加列 没有索引。
并将其添加为最后一列!
如果不是最后一列,它可能会尝试移动数据

批量保护事务日志更新
100只是一个例子

update top (100) table 
set newCol = oldCol 
where newCol is null  

如果已编入索引,则在此处添加 然后用

结束
update table 
set newCol = oldCol 
where newCol <> oldCol 

然后将newCol重命名为oldCol

这就是我如何循环来包含事务日志
通常以10000块为单位

declare @rowCount Int;
Set @rowCount = 1
While @rowCount > 0
Begin
   update top (1) [test].[dbo].[DateDateTime] 
   set [ddateTimeNoTime] = '2014-11-12'
   where [ddateTimeNoTime] <> '2014-11-12'
   set @rowCount = @@rowcount;
End