游标与While循环 - SQLServer

时间:2010-10-26 19:58:30

标签: sql sql-server

假设我在数据库中有一堆行(在本例中为SQLServer 2008),可用于创建方程式。

 -----------------------------------------------------
 OperationID | EquationID | Operation | Amount | Order
 -----------------------------------------------------
     1       |     1      |     +     |   12   |  1 
     2       |     1      |     +     |   12   |  2 
     3       |     2      |     /     |   2    |  3 
     4       |     2      |     +     |   12   |  1 
     5       |     2      |     -     |   2    |  2 
 -----------------------------------------------------

我需要想出一种方法来评估此表中的方程式。

  

等式1:12 + 12 = 24
  等式2:(12-2)/ 2 = 5

我无法想到一种在不迭代行的情况下获得这些结果的方法。我知道如何执行此操作的唯一方法是使用游标或使用临时表和while循环。有没有更好的方法来做到这一点?如果不是通常会执行更好的游标或循环?

注意:这有点简化,在项目的这个阶段,我们只能猜测数据的样子。假设每个'方程'将有大约100到1000次操作,并且每天将有几千个“方程式”需要处理。

2 个答案:

答案 0 :(得分:3)

已经证明,递归CTE比循环运行总计更好。这只是一个运行总计,实际上是一个变量运算符,因此性能优势应该适用于此。

创建一个行为类似于循环的递归CTE的方法是这样的:

        ;WITH cte AS (
        SELECT equation, number, order FROM table WHERE order = 1
        UNION ALL
        SELECT table.equation, 
            CASE WHEN table.operation = '+' THEN cte.number + table.number
                 WHEN table.operation = '-' THEN cte.number - table.number END AS number, --etc.
table.order FROM table INNER JOIN cte ON table.order = cte.order + 1 AND table.equation = cte.equation
        )
    SELECT equation, number, order 
    FROM cte
    OPTION (MAXRECURSION 1000);

第一个SELECT抓取你最左边的数字,UNION全部对它返回的数字进行以下操作。 maxrecursion选项将一个等式中的操作数限制为1000.当然,您可以将其设置得更高。

这个答案有些不完整,因为最终的选择查询会返回中间结果。这虽然过滤相当简单。

答案 1 :(得分:3)

我已经清理/充实了mootinator's answer并在此处展示了这些代码。我已经标记了这个答案社区wiki,因为mootinator值得称赞答案。这只是在不编辑答案的情况下呈现代码的最简单方法。

declare @equations table (
    OperationID int,
    EquationID int,
    Operation char(1),
    Amount int,
    [Order] int
)

insert into @equations
    (OperationID, EquationID, Operation, Amount, [Order])
    values 
    (1, 1, '+', 12, 1),
    (2, 1, '+', 12, 2),
    (3, 2, '/',  2, 3),
    (4, 2, '+', 12, 1),
    (5, 2, '-',  2, 2)

;with cteCalc as (
    select EquationID, Amount, [Order] 
        from @equations 
        where [Order] = 1
    union all
    select e.equationid, 
           case when e.Operation = '+' then c.Amount + e.Amount
                when e.Operation = '-' then c.Amount - e.Amount
                when e.Operation = '*' then c.Amount * e.Amount
                when e.Operation = '/' then c.Amount / e.Amount
           end AS Amount, 
           e.[Order] 
        from @equations e 
            inner join cteCalc c 
                on e.EquationID= c.EquationID
        where e.[Order] = c.[Order] + 1
),
cteMaxOrder as (
    select EquationID, MAX([Order]) as MaxOrder 
        from cteCalc 
        group by EquationID
)
select c.EquationID, c.Amount
    from cteMaxOrder mo
        inner join cteCalc c
            on mo.EquationID = c.EquationID
                and mo.MaxOrder = c.[Order]
    order by c.EquationID
    option (maxrecursion 1000)