假设我在数据库中有一堆行(在本例中为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次操作,并且每天将有几千个“方程式”需要处理。
答案 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)