【SQL】過去直近データを取得するには

■ はじめに

SQLで、過去直近データを取りたい時って多々あるのでメモ。

目次

【1】方法1
 1)構文
 2)欠点
【2】方法2
 1)構文
 2)サンプル
【3】方法3:ROW_NUMBER + PARTITION BY 

 【1】方法1

* ただし、この方法には欠点がある

1)構文

SELECT
  * 
FROM
  テーブル名
WHERE
  カラム名 = (SELECT MAX(カラム名) FROM テーブル名 WHERE カラム名 <= 対象日付)

 2)欠点

 * メインのSQLに他に条件を加えると「(SELECT MAX(カラム名)・・・」の方にも
   同条件を追加しなくてはならない
  => ただ、逆に言うと、同条件を追加すればいいのだが...

ダメな例

 * 以下のサンプルのデータにおいて、
  「id = 'I0000001'(apple)」の過去「2016-01-03」の直近データを取得しようと思った場合、
   以下のSQL文では0件データとなる

 ダメなサンプル

-- For SQL Server2008 R2

DECLARE @target_date datetime;
SET @target_date =  '2016-01-03';

SELECT
  i.id,
  i.name,
  i.price,
  i.updatedate
FROM
 item AS i 
WHERE
 updatedate = 
  (SELECT MAX(updatedate) AS latest FROM item WHERE updatedate <= @target_date)
  AND id = 'I0000001'

 【2】方法2

 * 以下の関連記事「最新日付データを取得する」を応用するだけ

最新日付データを取得するには
https://dk521123.hatenablog.com/entry/2014/10/09/004105

1)構文

SELECT
  * 
FROM
  テーブル名 AS A1
INNER JOIN
 (SELECT
    主キー,
    MAX(カラム名) AS latest
  FROM
    テーブル名
  WHERE
    カラム名 <= 対象日付
  GROUP BY
    主キー)) AS A2
ON
 A1.主キー = A2.主キー
 AND A1.カラム名 = A2.latest

2)サンプル

* 以下のサンプルは、SQL Server2008 R2で試したもの

サンプルデータ

-- テーブル定義
CREATE TABLE item
(
  id char(8),
  name varchar(100),
  price int,
  updatedate datetime
);

-- データ
INSERT INTO
 item(id, name, price, updatedate)
VALUES 
 ('I0000001', 'apple', 100, '2014-01-02 12:20:22'),
 ('I0000001', 'apple', 100, '2014-01-02 13:20:22'),
 ('I0000001', 'apple', 101, '2014-11-22 12:23:22'),
 ('I0000001', 'apple', 99, '2015-10-02 12:10:23'),
 ('I0000001', 'apple', 120, '2015-12-02 12:23:22'),
 ('I0000002', 'Orange', 62, '2014-01-01 12:20:22'),
 ('I0000002', 'Orange', 76, '2014-03-02 13:20:22'),
 ('I0000002', 'Orange', 92, '2014-11-22 12:23:22'),
 ('I0000002', 'Orange', 101, '2015-02-02 12:10:23'),
 ('I0000002', 'Orange', 67, '2015-12-02 12:23:22'),
 ('I0000002', 'Orange', 67, '2016-01-02 12:23:22');

SQL

DECLARE @target_date datetime;
SET @target_date =  '2016-01-03';

SELECT
 i.id,
 i.name,
 i.price,
 i.updatedate
FROM
 item AS i 
INNER JOIN
 (SELECT
  id,
  MAX(updatedate) AS latest
 FROM
  item
 WHERE
 id = 'I0000001' AND updatedate <= @target_date
 GROUP BY
  id) AS m
ON
 m.id=i.id
 AND m.latest = i.updatedate
ORDER BY
 id;

出力結果

id   name    price   updatedate
I0000001    apple   120 2015-12-02 12:23:22.000

【3】方法3:ROW_NUMBER + PARTITION BY

* 以下の関連記事を参照のこと。

https://dk521123.hatenablog.com/entry/2021/03/11/210937

関連記事

サブクエリ / 副問合せ
https://dk521123.hatenablog.com/entry/2015/12/21/002727
ROW_NUMBER + PARTITION BY
https://dk521123.hatenablog.com/entry/2021/03/11/210937
最新日付データを取得するには
https://dk521123.hatenablog.com/entry/2014/10/09/004105