我有一个包含一些值列,公式和结果列的表。
|rownum|value1|value2|value3|formula |result|
|------|------|------|------|--------------------|------|
|1 |11 |30 |8 |value1/value2*value3| |
|2 |43 |0 |93 |value1-value2+value3| |
我想使用公式的结果填充result
列。
目前我正在使用此查询执行此操作:
DECLARE @v_sql NVARCHAR(MAX)
SET @v_Sql = CAST ((SELECT
' UPDATE [table] ' +
' SET [result] = ' + table.[formula] +
' WHERE [rownum] = ' + CAST(table.[rownum] as nvarchar(255)) +
';'
FROM [table]
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)') AS NVARCHAR (MAX))
EXEC (@v_Sql)
问题是这需要很长时间。表中的#行将为5到1000万。
有没有办法加快速度?解决这个问题的替代方法是什么?
非常感谢!
答案 0 :(得分:3)
假设操作员订单规则并且仅涵盖您的简单公式示例:
UPDATE [table]
SET [result] = case replace(replace(replace([formula],'value1', ''), 'Value2', ''), 'Value3', '')
when '++' then [value1] + [value2] + [Value3]
when '+-' then [value1] + [value2] - [Value3]
when '+*' then [value1] + [value2] * [Value3]
when '+/' then [value1] + [value2] / [Value3]
when '-+' then [value1] - [value2] + [Value3]
when '--' then [value1] - [value2] - [Value3]
when '-*' then [value1] - [value2] * [Value3]
when '-/' then [value1] - [value2] / [Value3]
when '*+' then [value1] * [value2] + [Value3]
when '*-' then [value1] * [value2] - [Value3]
when '**' then [value1] * [value2] * [Value3]
when '*/' then [value1] * [value2] / [Value3]
when '/+' then [value1] / [value2] + [Value3]
when '/-' then [value1] / [value2] - [Value3]
when '/*' then [value1] / [value2] * [Value3]
when '//' then [value1] / [value2] / [Value3]
end
from [Table]
答案 1 :(得分:1)
想到两件简单的事情:
如果您要分别更新每一行,请确保rownum
列上有索引。
如果只有少数不同的公式,您可以在一个UPDATE
中使用相同的公式更新所有行,而不是单独更新每一行。在这种情况下,formula
列上的索引会有所帮助。
答案 2 :(得分:1)
通过公式类型更快地批量更新? [公式]所需的指数也是:
DECLARE @v_sql NVARCHAR(MAX)
SET @v_Sql = CAST ((SELECT
' UPDATE [table] ' +
' SET [result] = ' + [table].[formula] +
' WHERE [formula] = ''' + [table].[formula] + ''';'
FROM [table]
group by [table].[formula]
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)') AS NVARCHAR (MAX))
exec(@v_Sql)
答案 3 :(得分:0)
使用触发器选项,但就目前而言,更新块的影响会更小。
TOP(5000)
每次只会更新5000行WHERE [result] is null OR [result]=''
GO 20000
将执行此查询20000次(1000万行)它将继续执行,直到返回UPDATE语句的0条记录。
DECLARE @v_sql NVARCHAR(MAX)
SET @v_Sql = CAST ((SELECT
' UPDATE TOP (5000) [table] ' +
' SET [result] = ' + [table].[formula] +
' WHERE [formula] = ''' + [table].[formula] + '''
AND ([result] is null OR [result]='');'
FROM [table]
group by [table].[formula]
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)') AS NVARCHAR (MAX))
exec(@v_Sql)
GO 20000
在此之后,创建触发器
答案 4 :(得分:0)
感谢所有的回复和想法。最后,通过在维度而不是事实表中保存公式来解决问题。 这将为维度中的每一行生成1个更新语句,并将其应用于具有where子句的所有相关事实行,而不是每个事实行的1个更新语句。 处理时间从>开始下降。 1.5小时到不到一秒钟。
答案 5 :(得分:0)
我刚创建了500万行的表。表结构为:
rn t1 t2 t3 formula
1 80 23 93 t1 / t2 * t3
2 80 87 30 t1 / t2 * t3
3 92 83 63 t1 / t2 * t3
4 68 19 36 t1 / t2 * t3
5 65 63 10 t1 / t2 * t3
如果您确定,所有公式都是有效的,并且您不会将除以零或数据类型溢出,在这种情况下,您可以在SQL Server中创建自己的eval()函数。
我在公式中为3个值创建了自己的函数,其符号如下: '+',' - ','*','/'。
功能代码是:
use db_test;
go
alter function dbo.eval(@a varchar(max))
returns float
as
begin
set @a = replace(@a, ' ', '');
declare @pos1 int = PATINDEX('%[+/*-]%', @a);
declare @t1 float = cast(substring(@a, 1, @pos1 - 1) as float);
declare @sign1 char(1) = substring(@a, @pos1, 1);
set @a = substring(@a, @pos1 + 1, len(@a) - @pos1);
declare @pos2 int = PATINDEX('%[+/*-]%', @a);
declare @t2 float = cast(substring(@a, 1, @pos2 - 1) as float);
declare @sign2 char(1) = substring(@a, @pos2, 1);
set @a = substring(@a, @pos2 + 1, len(@a) - @pos2);
declare @t3 float = cast(@a as float);
set @t1 = (
case @sign1
when '+' then @t1 + @t2
when '-' then @t1 - @t2
when '*' then @t1 * @t2
when '/' then @t1 / @t2
end
);
set @t1 = (
case @sign2
when '+' then @t1 + @t3
when '-' then @t1 - @t3
when '*' then @t1 * @t3
when '/' then @t1 / @t3
end
);
return @t1;
end;
它适用于下一个数据:
select dbo.eval('7.6*11.3/4.5') as eval, 7.6*11.3/4.5 as sqlServerCalc;
eval sqlServerCalc
19,0844444444444 19.084444
之后,您可以按列值替换公式中的值并计算它:
with cte as (
select rn, t1, t2, t3, formula,
REPLACE(REPLACE(REPLACE(formula, 't1', cast(t1 as varchar(max))), 't2', cast(t2 as varchar(max))), 't3', cast(t3 as varchar(max))) as calc
from db_test.dbo.loop
)
select rn, t1, t2, t3, formula, db_test.dbo.eval(calc) as result
into db_test.dbo.loop2
from cte;
时间对我来说没问题,我的Sql Server 2016需要3分钟才能获得好结果:
select top 5 *
from db_test.dbo.loop2;
rn t1 t2 t3 formula result
1 80 23 93 t1 / t2 * t3 323,478260869565
2 80 87 30 t1 / t2 * t3 27,5862068965517
3 92 83 63 t1 / t2 * t3 69,8313253012048
4 68 19 36 t1 / t2 * t3 128,842105263158
5 65 63 10 t1 / t2 * t3 10,3174603174603
如果您有公式中适用的所有操作的列表,则可以为多个变量编写公共函数。但是,如果公式中有更复杂的东西,那么应该使用CLR。