【初級】履歴を持つレコードの取り扱い方法

たぶんよく使うSQLです。
あるデータを履歴管理する場合のデータの抽出方法です。以下のようなエンティティを作るとします。

国識別テーブル

識別番号 国名 属性
A A共和国 ライトサイド
B B帝国 ダークサイド
C C自治区 ライトサイド

戦況履歴テーブル

履歴番号 識別番号 状況 人数
1 A 突撃 1000
2 A 進行 900
3 A 退却 300
4 B 突撃 900
5 A 突撃 500
6 B 退却 800
7 C 進行 710
8 B 進行 850

 

このとき、「属性がライトサイドの『国名』と最新の『状況』とその時の『人数』」を確認したい場合、以下のようなSQLになります。

  SELECT
      X.国名,
      Y.状況,
      Y.人数
  FROM
      国識別テーブル X
  INNER JOIN 戦況履歴テーブル Y ON
      Y.識別番号 = X.識別番号
  AND Y.履歴番号 = ( SELECT MAX(Z.履歴番号) FROM 戦況履歴テーブル Z WHERE Z.識別番号 = Y.識別番号 ) 
  WHERE
  X.属性 = 'ライトサイド'

ここでは『最新』を「戦況履歴テーブルの履歴番号の最大」と定義しています。
他のにも「戦況履歴テーブルの履歴番号降順の1件目」という定義の仕方があります。その場合は、
例えばPostgreSQLやMySQLなどでは

  AND Y.履歴番号 = ( SELECT Z.履歴番号 FROM 戦況履歴テーブル Z WHERE Z.識別番号 = Y.識別番号 ORDER BY Z.履歴番号 DESC LIMIT 1) 

Oracleでは

  AND Y.履歴番号 = ( SELECT ZZ.履歴番号 FROM (SELECT Z.履歴番号 FROM 戦況履歴テーブル Z WHERE Z.識別番号 = Y.識別番号 ORDER BY Z.履歴番号 DESC) ZZ WHERE ROWNUM = 1) 

と表現することもできます。どの表現にするかは、業務の場合は性能要件や保守要件等を考慮して設計するようにしましょう。(インデックスの設定や永年同じDBを使い続けるか等)

結果

国名 状況 人数
A共和国 突撃 500
C自治区 進行中 710

 

ここでのポイント:
戦況履歴テーブルの「識別番号」毎に最大の「履歴番号」を取得するようにします。

次に、「最後に退却した『国名』と『人数』と『履歴番号』」を確認したい場合はどうでしょう?以下のようなSQLになります。

SELECT
    X.国名,
    Y.人数,
    Y.履歴番号
FROM
    国識別テーブル X
INNER JOIN 戦況履歴テーブル Y ON
    Y.識別番号 = X.識別番号
AND Y.履歴番号 = ( SELECT MAX(Z.履歴番号) FROM 戦況履歴テーブル Z WHERE Z.状況 = '退却' )

結果

国名 人数 履歴番号
B帝国 800 6

 

ここでのポイント:
戦況履歴テーブルのみを条件にしているので、先ほどとは違い「識別番号」を条件に含まないようにします。

では、「900人未満で最初に進行した『国名』と『人数』」はどのようなSQLで確認できるでしょうか?

タイトルとURLをコピーしました