web-dev-qa-db-ja.com

著者ごとに最新の投稿を1つ選択してください

簡単な質問だと思います。何度も尋ねられたと思いますが、他の回答からはわかりません。

PostgreSQLとMySQLの最新バージョンを使用しています。 2つのテーブルがあります。

CREATE TABLE authors (
    id INT,
    name VARCHAR
)

CREATE TABLE posts (
    id INT,
    author_id INT,
    text VARCHAR,
    date DATE
)

著者ごとに最新の投稿を1つ選択する必要があります。ありがとう!

[〜#〜]更新[〜#〜]

おかげで、両方のリンクはいくつかの例外を除いて私の質問への回答を提供します。次のすべてのクエリは同じ結果を返します(どちらが最も効率的ですか?)問題は、同じ日付の同じ著者からの投稿が複数ある場合です。次に、返される結果セットには、そのような投稿がすべて含まれます。これらのクエリを変更して、著者ごとに1つの投稿を返すexactlyにはどうすればよいですか?

SELECT p1.*
FROM posts p1
LEFT JOIN posts p2 ON p1.author_id = p2.author_id AND p1.date < p2.date
WHERE p2.author_id IS NULL
ORDER BY p1.author_id;

SELECT p1.* 
FROM posts p1
INNER JOIN (
  SELECT author_id, MAX(date) AS max_date
  FROM posts
  GROUP BY author_id) p2
  ON p1.author_id = p2.author_id AND p1.date = p2.max_date
ORDER BY p1.author_id;

SELECT *
FROM posts p1
WHERE date = (SELECT MAX(p2.date)
              FROM posts p2
              WHERE p1.author_id = p2.author_id)
ORDER BY author_id;

SELECT * FROM (
    SELECT author_id, MAX(date) date
    FROM posts GROUP BY author_id
) p1 INNER JOIN posts p2 USING (author_id, date)
ORDER BY author_id;
5
Igor

クエリを最大限の効率で使用することを目的としている場合、上記のクエリはどれも実際には最適ではありません。常にではありません。

効率は、特定のDBMS、特定のバージョン(異なるバージョンではオプティマイザと使用可能な構文にさまざまな改良が加えられています)、列のタイプ、使用可能なインデックス、テーブルのサイズと値の分布、サーバーが実行しているハードウェア、構成設定など.

常にクエリのさまざまな書き方をテーブル上でテストし、ハードウェアと構成設定を使用して、本番環境で予想されるサイズと分散を使用して、保持する必要のあるクエリの書き換えを決定する必要があります。

この特定の種類のクエリは、多くの場合 _greatest-n-per-group_ と呼ばれ(それにタグさえあります!)、特定の前提の下では、多くの書き込み方法の1つが両方で非常に効率的です。 MySQLおよびPostgreSQL。 PostgresのLATERAL結合を使用します。これは、9.3以降のバージョン(SQL Serverの言語では_CROSS/OUTER APPLY_)で利用可能であり、MySQLでのこの結合のシミュレーションです。

投稿数(グループ化を適用するテーブル)に比べて、投稿者(グループ化する属性)の数は少ないと想定されています。また、すべての個別の_author_id_値を検索するためのインデックスまたはテーブルがあり、グループ化用のpostsテーブルに追加のインデックスがある場合も最適です。

グループごとの最大の問題に対するこのソリューションは、グループごとに常に1つの結果を返すため、同点に関する要求にも一致します。 (同点の)どれが返されるかを正確にしたい場合は、サブクエリの_ORDER BY_を(たとえば、_ORDER BY pi.date DESC, pi.id DESC_または_ORDER BY pi.date DESC, a.name_に)変更できます。

PostgreSQLでのクエリ:

_SELECT p.* 
FROM authors AS a
   , LATERAL 
       ( SELECT pi.*
         FROM posts AS pi
         WHERE pi.author_id = a.author_id
         ORDER BY pi.date DESC
         LIMIT 1
       ) AS p ;
_

MySQLでのクエリ:

_SELECT p.* 
FROM authors AS a
  JOIN posts AS p
    ON p.id =
       ( SELECT pi.id
         FROM posts AS pi
         WHERE pi.author_id = a.author_id
         ORDER BY pi.date DESC
         LIMIT 1
       ) ;
_

有用なインデックスは、MySQLの場合はposts (author_id, date, id)にあり、Postgresの場合はposts (author_id, date DESC)にあります。


言うまでもありませんが、上記のいずれかを使用する前に、それらを環境でテストし、クエリの他のすべてのバージョン/書き換えに対してクロステストする必要があります。たとえばPostgresでは、_DISTINCT ON_構文は9.3より前のバージョンで使用できます。結果のクエリは、LATERALよりもコンパクトで、さまざまなデータ分布の下でより効率的になる可能性があります。クエリ:

_SELECT DISTINCT ON (author_id) p.*
FROM posts AS p
ORDER BY p.author_id,
         p.date DESC ;
_
16
ypercubeᵀᴹ