web-dev-qa-db-ja.com

MySQLでラグ関数をシミュレートする

| time                | company | quote |
+---------------------+---------+-------+
| 0000-00-00 00:00:00 | GOOGLE  |    40 |
| 2012-07-02 21:28:05 | GOOGLE  |    60 |
| 2012-07-02 21:28:51 | SAP     |    60 |
| 2012-07-02 21:29:05 | SAP     |    20 |

MySQLのこのテーブルでラグを実行して、引用符の違いを出力するにはどうすればよいですか。たとえば:

GOOGLE | 20
SAP    | 40  
32
javanx

これは私のお気に入りのMySQLハックです。

これは、遅延関数をエミュレートする方法です。

SET @quot=-1;
select time,company,@quot lag_quote, @quot:=quote curr_quote
  from stocks order by company,time;
  • lag_quoteは、前の行の引用符の値を保持します。最初の行の@quotは-1です。
  • curr_quoteは、現在の行の引用符の値を保持します。

ノート:

  1. order by句は、通常のウィンドウ関数の場合と同様にここで重要です。
  2. 同じcompanyの引用符の違いを確実に計算するために、companyにラグを使用することもできます。
  3. 同じ方法で行カウンターを実装することもできます@cnt:=@cnt+1

このスキームの良いところは、集計関数、ストアドプロシージャの使用やアプリケーションサーバーでのデータ処理など、他のアプローチと比較して計算上非常に無駄がないことです。

編集:

今、あなたが言及した形式で結果を得るというあなたの質問に来ます:

SET @quot=0,@latest=0,company='';
select B.* from (
select A.time,A.change,IF(@comp<>A.company,1,0) as LATEST,@comp:=A.company as company from (
select time,company,quote-@quot as change, @quot:=quote curr_quote
from stocks order by company,time) A
order by company,time desc) B where B.LATEST=1;

ネストは相互に関連していないため、(計算上)見た目ほど(構文的に)悪くはありません:)

これに関して何か助けが必要な場合はお知らせください。

48
Dojo

MySQL 8.0以降では、LAGをシミュレートする必要はありません。ネイティブにサポートされていますが、

ウィンドウ関数

パーティション内の現在の行からN行だけ遅れる(先行する)行からexprの値を返します。そのような行がない場合、戻り値はデフォルトです。たとえば、Nが3の場合、戻り値は最初の2行のデフォルトです。 Nまたはデフォルトが欠落している場合、デフォルトはそれぞれ1およびNULLです。

SELECT
     company,
     quote,
     LAG(quote) OVER(PARTITION BY company ORDER BY time) AS prev_quote
FROM tab;

DBFiddle Demo

8
Lukasz Szozda

目的の結果を得るには、まず各企業の最後のタイムスタンプと最後から2番目のタイムスタンプを見つける必要があります。次のクエリを使用すると、非常に簡単です。

SELECT c.company, c.mts, max(l.ts) AS lts
  FROM (SELECT company, max(ts) AS mts FROM cq GROUP BY company) AS c
  LEFT JOIN cq l
    ON c.company = l.company AND c.mts > l.ts
 GROUP BY c.company, c.mts;

次に、このサブクエリを元のテーブルと結合して、目的の結果を取得する必要があります。

SELECT c.company, l.quote, coalesce(l1.quote, 0),
       (l.quote - coalesce(l1.quote, 0)) AS result
  FROM (SELECT c.company, c.mts, max(l.ts) AS lts
      FROM (SELECT company, max(ts) AS mts FROM cq GROUP BY company) AS c
      LEFT JOIN cq l
        ON c.company = l.company AND c.mts > l.ts
     GROUP BY c.company, c.mts) AS c
  LEFT JOIN cq AS l ON l.company = c.company AND l.ts = c.mts
  LEFT JOIN cq AS l1 ON l1.company = c.company AND l1.ts = c.lts;

SQL Fiddle で結果を確認できます。

このクエリは標準のSQL機能のみを使用しており、どのRDBMSでも機能するはずです。

4
vyegorov