履歴テーブルにすべての履歴イベントを記録するドキュメント管理システムがあります。特定のクライアントの特定の日付のステータスが 5 である最も古い doc_id を提供できるようにするよう求められました。表は次のようになります (簡単にするために切り捨てられています)。
doc_history:
id integer
doc_id integer
event_date timestamp
client_id integer
status_id integer
client_id 列と status_id 列は、イベント発生後のドキュメントの値です。これは、doc_id で定義されたドキュメントの最大履歴イベント行が、ドキュメント テーブルの同じ列と一致することを意味します。特定のイベント日付でイベントを制限すると、その時点でのドキュメントの値を確認できます。これらの値は静的ではないため、status_id が 5 の特定の client_id を単純に検索することはできません。見つかった結果がドキュメントの max(id) と一致しない可能性があるからです。うまくいけば、それはある程度の意味があります。
私が動作することがわかったが、遅いのは次のとおりです。
select
t.*
from
(select
distinct on (doc_id),
*
from
doc_history
where
event_date <= '2013-02-17 23:59:59'
order by
doc_id, id desc) t
where
t.client_id = 9999 and
t.status_id = 5
limit 1;
基本的に、指定された最大イベント日付の前に特定のドキュメント ID の最大 ID を取得し、その最大履歴アイテムが指定されたクライアントに割り当てられ、ステータスが 5 に設定されていることを確認しています。
私のやり方の欠点は、すべてのクライアントのすべての履歴レコードをスキャンして最大値を取得し、1 つのクライアントとステータスについて探しているものを見つけることです。現在のところ、これは約 1506 万行をスキャンし、私の開発サーバーでは約 90 秒かかります (これは非常に高速ではありません)。
さらに複雑なことに、これを前の週の各日、つまり実行ごとに合計 7 回行う必要があります。さらに、システム内のすべてのドキュメントは、新規を表すステータス 5 で始まります。これにより、このクエリは、そのクライアントに対して入力された最初のドキュメントを返すだけになります。
select * from doc_history where client_id = 9999 and
status_id = 5 and
event_date <= '2013-02-17 23:59:59'
order by id limit 1;
私が望んでいるのは、最初にすべてのクライアントのすべてのドキュメント ID の最大 ID を見つける必要なく、特定のクライアントとステータス値に一致する特定のドキュメントの最大履歴レコードが見つかるまでスキャンすることです。これがウィンドウ関数 (partition by) や、現在表示されていないその他のロジックで実行できるかどうかはわかりません。
doc_history テーブル内のイベントの 1 つの例:
# select id, doc_id, event, old_value, new_value, event_date, client_id, status_id from doc_history where doc_id = 9999999 order by id;
id | doc_id | event | old_value | new_value | event_date | client_id | status_id
----------+---------+-------+-----------+-----------+----------------------------+-----------+-----------
25362415 | 9999999 | 13 | | | 2013-02-14 11:49:50.032824 | 9999 | 5
25428192 | 9999999 | 15 | | | 2013-02-18 11:15:48.272542 | 9999 | 5
25428193 | 9999999 | 7 | 5 | 1 | 2013-02-18 11:15:48.301377 | 9999 | 1
イベント 7 はステータスが変更されたもので、古い値と新しい値は、ステータスが 5 から 1 に変更されたことを示しており、status_id 列に反映されています。event_date が 2013-02-17 23:59:59 以下の場合、上記のレコードは status_id が 5 の最も古い「NEW」ドキュメントになりますが、2013 年 2 月 17 日以降はそうではありません。