7

ID、日、時間、金額を含む一連の行を返す複雑なクエリ (複数の結合、ユニオンを含む) があります。クエリの出力は次のようになります。

id day    hr  amount 
1   1      1   10       
1   1      2   25       
1   1      3   30        
1   2      1   10       
1   2      2   40       
1   2      2   30        
2   1      1   10       
2   1      2   15        
2   1      3   30       
2   2      1   10       
2   2      2   20      
2   2      2   30  

1日の時間ごとに、各IDの累積合計を見つける必要があります。出力は次のようになります。

id day    hr  amount cumulative total
1   1      1   10       10
1   1      2   25       35
1   1      3   30       65 
1   2      1   10       10
1   2      2   40       50
1   2      2   30       80 
2   1      1   10       10
2   1      2   15       25 
2   1      3   30       55
2   2      1   10       10
2   2      2   20       30
2   2      2   30       60

最初の出力を生成する最初のクエリは次のようになります。

select id, day, hr, amount from
( //multiple joins on multiple tables)a
left join
(//unions on multiple tables)b
on a.id=b.id;

2 番目の出力で説明されている累積合計を取得する SQL クエリは何ですか? ソリューションでは SET を使用しないでください。

ありがとう。

4

3 に答える 3

12

アップデート

MySQL 8.0 では、「ウィンドウ関数」、SQL Server の「ウィンドウ関数」と同等の機能 (Transact-SQLOVER構文によってパーティション分割と順序付けが提供される)、および Oracle の「分析関数」が導入されています。

MySQL リファレンス マニュアル 12.21 ウィンドウ関数https://dev.mysql.com/doc/refman/8.0/en/window-functions.html

ここで提供される答えは、8.0 より前のバージョンの MySQL に対するアプローチです。


元の答え

MySQL は、他の DBMS (Oracle や SQL Server など) で利用可能な分析関数のような、実行中の「累積合計」を取得するために使用するタイプの分析関数を提供しません。

ただし、MySQL を使用して一部の分析関数をエミュレートすることは可能です。

(少なくとも) 2 つの実行可能なアプローチがあります。

1 つは、相関サブクエリを使用して小計を取得する方法です。このアプローチは、大規模なセットではコストがかかり、外部クエリの述語が複雑な場合は複雑になる可能性があります。「複数のテーブルでの複数の結合」がどれほど複雑かによって異なります。(残念ながら、MySQL も CTE をサポートしていません。)

もう 1 つのアプローチは、MySQL ユーザー変数を利用して、何らかのコントロール ブレーク処理を行うことです。ここでの「トリック」は、(ORDER BY を使用して) 並べ替えられたクエリの結果を別のクエリでラップすることです。

後者のアプローチの例を挙げます。

MySQL が操作を実行する順序のため、現在の行の値と現在の行の値をユーザー変数に保存するcumulative_total前に、列を計算する必要があります。この列を最初に置くのが最も簡単です。idday

i としてエイリアス化されたインライン ビュー (以下のクエリで) は、ユーザー変数がセッションで既に設定されている場合に備えて、ユーザー変数を初期化するためのものです。それらにすでに値が割り当てられている場合は、現在の値を無視する必要があります。これを行う最も簡単な方法は、それらを初期化することです。

以下の例では、元のクエリが括弧で囲まれ、エイリアスが与えられcます。元のクエリへの唯一の変更は ORDER BY 句の追加であるため、クエリからの行を順番に処理することを確認できます。

外側の選択は、現在の行の値が前の行と「一致」するidかどうかをチェックします。day一致する場合はamount、現在の行の値を累積小計に追加します。一致しない場合は、累積小計をゼロにリセットし、現在の行から金額を追加します (または、より単純に、現在の行から金額を割り当てるだけです)。

累積合計の計算が完了したら、現在の行のiddayの値をユーザー変数に保存して、次の行を処理するときに使用できるようにします。

例えば:

SELECT IF(@prev_id = c.id AND @prev_day = c.day
         ,@cumtotal := @cumtotal + c.amount
         ,@cumtotal := c.amount) AS cumulative_total
     , @prev_id  := c.id  AS `id`
     , @prev_day := c.day AS `day`
     , c.hr
     , c.amount AS `amount'
  FROM ( SELECT @prev_id  := NULL
              , @prev_day := NULL
              , @subtotal := 0
       ) i
  JOIN (

         select id, day, hr, amount from
         ( //multiple joins on multiple tables)a
         left join
         (//unions on multiple tables)b
         on a.id=b.id

         ORDER BY 1,2,3
       ) c

累積合計を最後の列として、列を別の順序で返す必要がある場合、1 つのオプションは、そのステートメント全体を括弧のセットでラップし、そのクエリをインライン ビューとして使用することです。

SELECT d.id
     , d.day
     , d.hr
     , d.amount
     , d.cumulative_total
FROM (
       // query from above
     ) d
于 2013-07-15T21:59:26.813 に答える