1

在存储过程中(我使用的是 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的每一行,从上到下重新计算Sold,条件为:每个ID的Sold总和不超过Price(结果如下)。

结果是这样的:

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

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

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

那么,通过这个任务,你能给我一个例子(使用 select form where group)来完成吗?还是我们有其他方法可以快速解决它?

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

我非常感谢您的帮助。

谢谢。

4

4 回答 4

3

为了完成您的任务,您需要计算运行总和并使用 case 语句

以前我使用 JOIN 来做运行总和和滞后与 case 语句

然而,使用递归 Cte 来计算Aaron Bertand描述的运行总数,以及Andriy M的case 语句,我们可以构建以下内容,它应该提供最佳性能并且不需要“查看前一行”

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; 

演示

于 2013-08-14T04:29:49.117 回答
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 的演示

让我分解查询。

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

  1. 第一个 SUM 根据 对行进行id分组,并在每个组中按 对行进行排序city and sold。该rows between子句告诉要考虑将哪些行加起来。在这里,我指定它来添加当前行和它上面的所有其他行。这给出了运行总和。
  2. 第二个做同样的事情,除了当前行被排除在加法之外。这实质上创建了一个运行总和,但比前一个总和滞后一行。

将此结果用作内联视图,外部选择使用CASE语句来确定新列的值。

  1. 只要运行总和小于或等于它给出的售价。
  2. 如果它与价格相交,则调整该值以使总和等于价格。
  3. 对于它下面的其余行,值设置为 0。

希望我的解释很清楚。

于 2013-08-14T05:37:36.463 回答
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 函数。不知道你打算如何计算价格。

于 2013-08-14T03:47:30.693 回答
0

对我来说,听起来你可以在这种情况下使用窗口函数。这适用吗?

尽管在我的情况下,您的最终结果可能如下所示:

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) 

取决于你想走多远。如果需要,你甚至可以使用 case 语句

于 2013-08-14T03:21:10.177 回答