4

車のMPGを計算するために使用される、以下のような単純なMySQLテーブルがあります。

+-------------+-------+---------+
| DATE        | MILES | GALLONS |
+-------------+-------+---------+
| JAN 25 1993 |  20.0 |    3.00 |
| FEB 07 1993 |  55.2 |    7.22 |
| MAR 11 1993 |  44.1 |    6.28 |
+-------------+-------+---------+

selectステートメントを使用して車のガロンあたりのマイル数(MPG)を簡単に計算できますが、MPGは充填ごとに大きく異なるため(つまり、毎回まったく同じ量のガスを充填しない)、コンピュータも「移動平均」です。したがって、どの行でも、MPGはその行のMILES / GALLONであり、MOVINGMPGは最後のN行のSUM(MILES)/ SUM(GALLONS)です。その時点までに存在する行がN未満の場合は、その時点までのSUM(MILES)/ SUM(GALLONS)だけです。

selectステートメントにNを代入することにより、MPGおよびMOVINGMPGで行をフェッチする単一のSELECTステートメントはありますか?

4

2 に答える 2

3

はい、単一のSQLステートメントで指定された結果セットを返すことができます。

残念ながら、MySQLは分析関数をサポートしていないため、かなり単純なステートメントになります。MySQLにはそれらをサポートする構文がありませんが、MySQLユーザー変数を使用して一部の分析関数をエミュレートすることは可能です。

(単一のSQLステートメントで)指定された結果セットを実現する方法の1つは、各行に一意の昇順整数値(rownum、クエリによって派生され、クエリ内で割り当てられる)を使用してJOIN操作を使用することです。

例えば:

SELECT q.rownum          AS rownum
     , q.date            AS latest_date
     , q.miles/q.gallons AS latest_mpg
     , COUNT(1)               AS cnt_rows
     , MIN(r.date)            AS earliest_date
     , SUM(r.miles)                AS rtot_miles
     , SUM(r.gallons)              AS rtot_gallons
     , SUM(r.miles)/SUM(r.gallons) AS rtot_mpg
  FROM ( SELECT @s_rownum := @s_rownum + 1 AS rownum
              , s.date
              , s.miles
              , s.gallons
           FROM mytable s
           JOIN (SELECT @s_rownum := 0) c
          ORDER BY s.date
       ) q
  JOIN ( SELECT @t_rownum := @t_rownum + 1 AS rownum
              , t.date                  
              , t.miles
              , t.gallons
           FROM mytable t
           JOIN (SELECT @t_rownum := 0) d
          ORDER BY t.date
       ) r
    ON r.rownum <= q.rownum
   AND r.rownum > q.rownum - 2
 GROUP BY q.rownum

GROUP BY各ロールアップ行に含める行数を指定するための「n」の目的の値は、句の直前の述語で指定されます。この例では、現在の合計行ごとに最大「2」行です。

値1を指定すると、(基本的に)元のテーブルが返されます。

「不完全な」実行中の合計行(「n」未満の行で構成される)を削除するには、「n」の値を再度指定して、次を追加する必要があります。

HAVING COUNT(1) >= 2

sqlfiddleデモ:http ://sqlfiddle.com/#!2/52420/2

ファローアップ:

Q: SQLステートメントを理解しようとしています。あなたのソリューションは、データベースの各行に対して20行の選択を行いますか?言い換えると、1000行ある場合、ステートメントは20000選択を実行しますか?(パフォーマンスが気になります)...

A:あなたはパフォーマンスに関心を持つ権利があります。

あなたの質問に答えるために、いいえ、これは1,000行に対して20,000の選択を実行しません。

パフォーマンスへの影響は、2つの(本質的に同一の)インラインビュー(別名qr)から生じます。MySQLがこれらを(基本的に)行うのは、一時的なMyISAMテーブル(MySQLでは「派生テーブル」と呼ばれます)を作成することです。これは基本的にmytable、追加の列を持つのコピーであり、各行には1から行数までの一意の整数値が割り当てられます。

2つの「派生」テーブルが作成されてデータが入力されると、MySQLはそれらの2つの「派生」テーブルを行ソースとして使用して外部クエリを実行します。からの各行は、rからqの最大行と照合されn、「現在の合計」マイルとガロンが計算されます。

パフォーマンスを向上させるには、クエリに一意の整数値を割り当てるのではなく、テーブルにすでに存在する列を使用できます。たとえば、date列が一意である場合、特定の期間の「現在の合計」を計算できます。

SELECT q.date                      AS latest_date
     , SUM(q.miles)/SUM(q.gallons) AS latest_mpg
     , COUNT(1)                    AS cnt_rows
     , MIN(r.date)                 AS earliest_date
     , SUM(r.miles)                AS rtot_miles
     , SUM(r.gallons)              AS rtot_gallons
     , SUM(r.miles)/SUM(r.gallons) AS rtot_mpg
  FROM mytable q
  JOIN mytable r
    ON r.date <= q.date
   AND r.date > q.date + INTERVAL -30 DAY
 GROUP BY q.date

(パフォーマンスのためdateに、インデックスの先頭の列としてで定義された適切なインデックスが必要になります。)


最初のクエリの場合、返される行数を減らすために(インラインビュー定義クエリに)含まれる述語(たとえば、過去1年間の日付値のみを返す)は、処理される行数を減らします。性能を上げる。


繰り返しになりますが、1,000行に対して20,000選択を実行することについての質問に対して、ネストされたループ操作は、同じ結果セットを取得する別の方法です。行数が多い場合、パフォーマンスが低下する可能性があります。(一方、返される行が数行しかない場合、このアプローチはかなり効率的です。

SELECT q.date                 AS latest_date
     , q.miles/q.gallons      AS latest_mpg
     , ( SELECT SUM(r.miles)/SUM(r.gallons)
           FROM mytable r
          WHERE r.date <= q.date
            AND r.date >= q.date + INTERVAL -90 DAY
       ) AS rtot_mpg
  FROM mytable q
 ORDER BY q.date
于 2013-02-21T00:24:49.897 に答える
0

このようなものが機能するはずです:

SELECT Date, Miles, Gallons, Miles/Gallons as MilesPerGallon,
  @Miles:=@Miles+Miles overallMiles,
  @Gallons:=@Gallons+Gallons overallGallons,
  @RunningTotal:=@Miles/@Gallons runningTotal
FROM YourTable
  JOIN (SELECT @Miles:= 0) t
  JOIN (SELECT @Gallons:= 0) s

SQLフィドルデモ

これにより、次のようになります。

DATE                MILES    GALLONS    MILESPERGALLON   RUNNINGTOTAL
January, 25 1993    20       3          6.666667         6.666666666667
February, 07 1993   55.2     7.22       7.645429         7.358121330724
March, 11 1993      44.1     6.28       7.022293         7.230303030303

- 編集 -

コメントに応じて、別の行番号を追加して、結果を最後のN行に制限できます。

SELECT *
FROM (
  SELECT Date, Miles, Gallons, Miles/Gallons as MilesPerGallon,
    @Miles:=@Miles+Miles overallmiles,
    @Gallons:=@Gallons+Gallons overallGallons,
    @RunningTotal:=@Miles/@Gallons runningTotal,
    @RowNumber:=@RowNumber+1 rowNumber
  FROM (SELECT * FROM YourTable ORDER BY Date DESC) u
    JOIN (SELECT @Miles:= 0) t
    JOIN (SELECT @Gallons:= 0) s
    JOIN (SELECT @RowNumber:= 0) r
  ) t
WHERE rowNumber <= 3

それに応じてORDERBY句を変更するだけです。そして、これが更新されたフィドルです。

于 2013-02-21T00:05:37.410 に答える