如何进行复杂计算作为此示例

时间:2013-08-14 03:06:50

标签: sql sql-server-2008

在存储过程中(我使用的是SQL server2008),我有这样的业务:

ID  City    Price   Sold    
1   A         10    3
1   B         10    5
1   A         10    1
1   B         10    3
1   C         10    5
1   C         10    2

2   A         10    1
2   B         10    6
2   A         10    3
2   B         10    4
2   C         10    3
2   C         10    4

我想做的是:

  • 每个ID,按城市排序。

  • 排序后,对于此ID的每一行,从上到下重新计算出售条件:每个ID的销售总额不超过价格(如下结果)。

结果如下:

ID  City    Price   Sold_Calculated 
1   A         10    3
1   A         10    1
1   B         10    5
1   B         10    1 (the last one equal '1': Total of Sold = Price)
1   C         10    0 (begin from this row, Sold = 0)
1   C         10    0

2   A         10    1
2   A         10    3
2   B         10    6
2   B         10    0 (begin from this row, Sold = 0)
2   C         10    0
2   C         10    0

现在,我正在使用Cursor执行此任务:获取每个ID,排序City,然后计算Sold,并保存到临时表。完成计算后,联合所有临时表。但这需要很长时间。

我知道人们建议的是,不要使用光标。

所以,通过这个任务,你能给我一个例子(使用选择形式,哪个组)完成?还是我们有其他方法可以快速解决?

我理解这项任务对你来说并不容易,但我仍然在这里发帖,希望有人帮助我完成。

我很感激你的帮助。

感谢。

4 个答案:

答案 0 :(得分:3)

为了完成你的任务,你需要计算一个运行总和并使用一个案例陈述

Previously我使用JOIN来执行运行总和和滞后的案例陈述

然而,使用递归Cte计算as described here的运行总计Aaron Bertandcase statement Andriy M我们可以构建以下内容,这应该提供最佳性能并且不需要“偷看前一行”

WITH cte 
     AS (SELECT Row_number() 
                  OVER ( partition BY id ORDER BY id, city, sold DESC) RN, 
                id, 
                city, 
                price, 
                sold 
         FROM   table1), 
     rcte 
     AS ( 
        --Anchor 
        SELECT rn, 
               id, 
               city, 
               price, 
               sold, 
               runningTotal = sold 
        FROM   cte 
        WHERE  rn = 1 
         --Recursion 
         UNION ALL 
         SELECT cte.rn, 
                cte.id, 
                cte.city, 
                cte.price, 
                cte.sold, 
                rcte.runningtotal + cte.sold 
         FROM   cte 
                INNER JOIN rcte 
                        ON cte.id = rcte.id 
                           AND cte.rn = rcte.rn + 1) 
SELECT id, 
       city, 
       price, 
       sold, 
       runningtotal, 
       rn, 
       CASE 
         WHEN runningtotal <= price THEN sold 
         WHEN runningtotal > price 
              AND runningtotal < price + sold THEN price + sold - runningtotal 
         ELSE 0 
       END Sold_Calculated 
FROM   rcte 
ORDER  BY id, 
          rn; 

DEMO

答案 1 :(得分:1)

正如@Gordon Linoff评论的那样,问题的排序顺序并不清楚。出于本答案的目的,我假设排序顺序为city, sold.

select id, city, price, sold, running_sum,
       lag_running_sum,
       case when running_sum <= price then Sold
            when running_sum > price and price > coalesce(lag_running_sum,0) then price - coalesce(lag_running_sum,0)
            else 0
       end calculated_sold
       from
       (
        select id, city, price, sold,
               sum(sold) over (partition by id order by city, sold
                               rows between unbounded preceding and current row) running_sum,
               sum(sold) over (partition by id order by city, sold
                               rows between unbounded preceding and 1 preceding) lag_running_sum
          from n_test
       ) n_test_running
 order by id, city, sold;

以下是Oracle的demo

让我打破查询。

我使用SUM作为分析函数来计算运行总和。

  1. 第一个SUM根据id对行进行分组,并在每个组中按city and sold对行进行排序。 rows between子句指出要考虑添加哪些行。在这里我已指定添加 当前行和它上面的所有其他行。这给出了运行总和。
  2. 第二个做同样的事情,除了,当前行被排除在加起来之外。这个 基本上创建了一个运行总和但是将前一个总和滞后一行。
  3. 将此结果用作内联视图,外部选择使用CASE语句来确定 新栏的价值。

    1. 只要运行金额小于或等于它所售出的价格。
    2. 如果它超过价格,则调整该值以使总和等于价格。
    3. 对于其下方的其余行,value设置为0.
    4. 希望我的解释非常清楚。

答案 2 :(得分:0)

对我来说,听起来你可以在这种情况下使用window functions。这适用吗?

虽然在我的情况下你的最终结果可能如下:

ID  City Price Sold_Calculated
2   A    10    4
2   B    10    6
2   C    10    0

可能有像

这样的聚合
SUM(Sold_Calculated) OVER (PARTITION BY ID, City, Price, Sold_Calculated) 

取决于您想要走多远..如果需要,您甚至可以使用案例陈述

答案 3 :(得分:0)

您是否希望在SQL中完全执行此操作?一个简单的方法是:

SELECT C.ID,
       C.City,
       C.Price,
       calculate_Sold_Function(C.ID, C.Price) AS C.Sold_Calculated
FROM CITY_TABLE C
GROUP BY C.City

其中 calculate_Sold_Function 是以ID和Price为参数的T-SQL / MySQL / etc函数。不知道你打算如何计算价格。