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

そして今、私は Cursor を使用してこのタスクを実行しています: 各 ID を取得し、都市を並べ替え、Sold を計算し、一時テーブルに保存します。計算が終了したら、すべての一時テーブルを結合します。しかし、それには長い時間がかかります。

人々がアドバイスしていることは、カーソルを使用しないでください。

では、このタスクを完了するための例を (select form where group を使用して) 教えていただけますか? または、それをすばやく解決する他の方法はありますか?

この作業があなたにとって簡単ではないことは理解していますが、それでもここに投稿します。誰かが私を助けてくれることを願っています.

私はあなたの助けにとても感謝しています.

ありがとう。

4

4 に答える 4

3

タスクを達成するには、実行中の合計を計算し、case ステートメントを使用する必要があります

以前は、JOIN を使用して累計を計算し、case ステートメントで Lag を実行していました。

ただし、ここAaron Bertandによって説明されているように、再帰的な Cte を使用して実行中の合計を計算し、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. 2 番目のものは、現在の行が合計から除外されることを除いて、同じことを行います。これにより、基本的に実行中の合計が作成されますが、前の合計より 1 行遅れます。

この結果をインライン ビューとして使用すると、外側の選択は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 と価格をパラメーターとして受け取る 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) 

どこまで行きたいかによって..必要に応じてケースステートメントを使用することもできます

于 2013-08-14T03:21:10.177 に答える