web-dev-qa-db-ja.com

Postgresqlによる効率的な最新レコードクエリ

大きなクエリを実行する必要がありますが、最新のレコードのみが必要です。

単一のエントリの場合、おそらく次のようなことをします

SELECT * FROM table WHERE id = ? ORDER BY date DESC LIMIT 1;

しかし、大量(数千エントリ)のレコードの最新レコードをプルする必要がありますが、最新エントリのみです。

これが私が持っているものです。あまり効率的ではありません。もっと良い方法があるかどうか疑問に思っていました。

SELECT * FROM table a WHERE ID IN $LIST AND date = (SELECT max(date) FROM table b WHERE b.id = a.id);
45
Sheldon Ross

データモデルを変更したくない場合は、DISTINCT ONを使用して、 "a"の各エントリのテーブル "b"から最新のレコードをフェッチできます。

SELECT DISTINCT ON (a.id) *
FROM a
INNER JOIN b ON a.id=b.id
ORDER BY a.id, b.date DESC

クエリで「ソート」を避けたい場合、次のようなインデックスを追加してくださいmight助けてくれますが、よくわかりません:

CREATE INDEX b_id_date ON b (id, date DESC)

SELECT DISTINCT ON (b.id) *
FROM a
INNER JOIN b ON a.id=b.id
ORDER BY b.id, b.date DESC

あるいは、何らかの方法でテーブル "a"からレコードをソートする場合:

SELECT DISTINCT ON (sort_column, a.id) *
FROM a
INNER JOIN b ON a.id=b.id
ORDER BY sort_column, a.id, b.date DESC

代替アプローチ

ただし、上記のクエリはすべてすべての参照行をテーブル "b"から読み取る必要があります。そのため、大量のデータがある場合は、やはり遅すぎる可能性があります。

a.idごとに最新の「b」レコードのみを保持する新しいテーブルを作成できます。または、それらの列を「a」テーブル自体に移動することもできます。

45
intgr

これはより効率的です。違い:テーブルbのクエリは1回だけ実行され、相関サブクエリはすべての行に対して実行されます。

SELECT * 
FROM table a 
JOIN (SELECT ID, max(date) maxDate
        FROM table
      GROUP BY ID) b
ON a.ID = b.ID AND a.date = b.maxDate
WHERE ID IN $LIST 
35
manji

メソッドについて-テーブルaの最新の更新/挿入時間を含む小さな派生テーブルを作成します-このテーブルa_latestを呼び出します。テーブルa_latestには、特定のクエリ要件を満たすために十分な粒度が必要です。あなたの場合、それは使用するのに十分でなければなりません

CREATE TABLE 
a_latest 
( id INTEGER NOT NULL, 
  date TSTAMP NOT NULL, 
  PRIMARY KEY (id, max_time) );

次に、najmeddineによって提案されたクエリと同様のクエリを使用します。

SELECT a.* 
FROM TABLE a, TABLE a_latest 
USING ( id, date );

トリックは、a_latestを最新の状態に保つことです。これは、挿入と更新のトリガーを使用して行います。 plppgsqlで記述されたトリガーは、非常に簡単に記述できます。ご希望の場合は、例を提供させていただきます。

ここでのポイントは、更新自体の最中に最新の更新時間の計算が処理されるということです。これにより、負荷がクエリから離れます。

4
youngthing

これについてどう思う?

select * from (
   SELECT a.*, row_number() over (partition by a.id order by date desc) r 
   FROM table a where ID IN $LIST 
)
WHERE r=1

過去によく使った

3
unknown

IDごとに多くの行がある場合は、必ず相関サブクエリが必要です。 IDごとに1つのインデックスルックアップを行いますが、これはテーブル全体をソートするよりも高速です。

何かのようなもの :

SELECT a.id,
(SELECT max(t.date) FROM table t WHERE t.id = a.id) AS lastdate
FROM table2;

ここで使用する 'table2'は、上記のクエリで言及したテーブルではありません。ここでは、パフォーマンスを向上させるために個別のIDのリストが必要です。 IDはおそらく別のテーブルへのFKであるため、これを使用します。

1
peufeu