在SQL Server中优化运行乘法计算

时间:2018-06-21 13:01:21

标签: sql-server

我有一个大约有2400条记录的大型数据库表,当我运行以下功能时:

SELECT (SELECT EXP(SUM(LOG((cast(t1.NAT as float) + ISNULL(cast(t1.Dist as float),0))/cast(t1.NAT as float)))) FROM Test t1 where t1.CODE = t2.CODE AND t1.DATE <= t2.DATE) as Distro FROM Test t2

上面的代码遍历每一行都会导致性能问题。有没有优化的方法?我有什么错误吗?

我使用此函数的表没有按DATE排序的数据,因此无法对其进行排序。

2 个答案:

答案 0 :(得分:0)

尝试使用以下JOIN版本的查询

SELECT 
    Distro=EXP(SUM(LOG( 1 +  ISNULL(cast(t1.Dist as float)/cast(t1.NAT as float),0))))
FROM 
Test t1 
    JOIN 
 Test t2
        On t1.CODE = t2.CODE AND t1.DATE <= t2.DATE

如果您的问题是函数,您也可以使用

获得相同的结果
DECLARE @result float=1
 SELECT 
        @result=@result*( 1 +  ISNULL(cast(t1.Dist as float)/cast(t1.NAT as float),0))
    FROM 
    Test t1 
        JOIN 
     Test t2
            On t1.CODE = t2.CODE AND t1.DATE <= t2.DATE

SELECT Distro=@result

答案 1 :(得分:0)

  

是否有一种优化方法?我有什么错误吗?

是的。

这是一个三角形联接。

您说该表有2,400行。为了简单起见,假设只有一个CODE,那么即使CODE, DATE上有一个索引,子查询平均也需要处理一半的表(日期最低的外排只会导致一行进行求和,但是到遇到最高日期时,它需要对整个2,400行进行求和。

因此,总计的总行数将为2,881,2002400 * 2401 / 2)。

您的情况可能不会那么糟-取决于您实际上拥有多少CODE以及他们分布得如何,但是仍然使用窗口函数会更有效,因为它们可以用一个传递数据。

假设CODE, DATE是唯一的,则可以使用

SELECT CASE 
         WHEN Min(Abs(input)) 
                OVER ( 
                  PARTITION BY Code ORDER BY DATE) = 0 THEN 0 
         ELSE CASE 
                WHEN Sum(Sign(CASE 
                                WHEN input < 0 THEN 1 
                                ELSE 0 
                              END)) 
                       OVER ( 
                         PARTITION BY Code ORDER BY DATE) % 2 = 1 THEN -1 
                ELSE 1 
              END * Exp(Sum(Log(Abs(NULLIF(input, 0)))) 
                          OVER ( 
                            PARTITION BY Code ORDER BY DATE)) 
       END 
FROM   Test t1
CROSS APPLY (VALUES (( CAST(t1.NAT AS FLOAT) + ISNULL(CAST(t1.Dist AS FLOAT), 0) ) / CAST(t1.NAT AS FLOAT))) V(input)