我在一个实时数据库中有一个SQL表,它有超过600万行,我想提高特定列的精度:
ALTER TABLE sales ALTER COLUMN amount DECIMAL(8,4)
现在我的问题是,如果我执行上面的查询,SQL Server会重新计算每个单元格并将新值写回同一个单元格吗? (如果是,那意味着它肯定需要很长时间才能执行,而我们在数据库上的其他活动也会受到影响。)或者,该语句是否会以其他方式执行?
答案 0 :(得分:6)
将decimal(8, 2)
转换为decimal(8, 4)
并不能实际提高精度;它增加了比例。
所以总的位数(以及存储要求)没有改变。
如果你有超过 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
表的读取。
然后:
sales_tmp
至sales
)如果您在受影响的表上设置了复制或其他奇特的东西,则可能会出现一些问题。遗憾的是,禁用和重新启用这些并不是一件容易的事。
如果您担心更新600万条记录对交易日志的影响,您需要批量更新记录。其大小将根据您的需要而有所不同。我会推荐1,000-10,000。
IMO,我不认为你真的需要担心这个问题,除非你的数据库服务器真的受到空闲空间的限制,但这些信息可能对未来有用。
如果您的恢复模型是SIMPLE,那么日志包含也是如此。如果它是完全的,那就更难了。
尽管Blam在他的回答中声称,他的循环实现绝对不保证任何包含事务日志文件。
在循环体的末尾发出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