web-dev-qa-db-ja.com

DISTINCT句を使用してデータをフィルタリングしますが、それでもDISTINCTではない他のフィールドをプルします

Postgresqlで、順序付けられたデータのセットをプルし、それを個別のフィールドでフィルタリングするクエリを作成しようとしています。また、同じテーブル行から他のいくつかのフィールドをプルする必要がありますが、それらは個別の評価から除外する必要があります。例:

  SELECT DISTINCT(user_id) user_id, 
         created_at 
    FROM creations 
ORDER BY created_at   
   LIMIT 20

user_idDISTINCTにする必要がありますが、created_atの日付が一意であるかどうかは関係ありません。 created_at日付が評価に含まれているため、結果セットに重複したuser_idが含まれています。

また、データは日付順に並べる必要があるため、ここではDISTINCT ONを使用することはできません。 DISTINCT ONフィールドがORDER BY句の最初のフィールドである必要があり、それは私が求める結果を提供しません。

DISTINCT句を適切に使用し、そのスコープを1つのフィールドのみに制限し、他のフィールドを選択するにはどうすればよいですか?

13
mindtonic

ご存知のとおり、標準SQLはDISTINCTを、1列または数列だけでなく、選択リスト全体に適用するものとして扱います。これは、DISTINCTから除外する列にどの値を入れるかがあいまいであるためです。同じ理由で、標準SQLでは、_GROUP BY_を使用したクエリにあいまいな列を含めることはできません。

しかし、PostgreSQLにはSQLの非標準の拡張機能があり、要求されていることを可能にします:DISTINCT ON (expr)

_SELECT DISTINCT ON (user_id) user_id, created_at 
FROM creations 
ORDER BY user_id, created_at   
LIMIT 20
_

ORDER BY句の左端の部分として、個別の式を含める必要があります。

詳細については、 DISTINCT句 のマニュアルを参照してください。

5
Bill Karwin

各ユーザーの最新のcreated_atが必要な場合は、次のように集計することをお勧めします。

SELECT user_id, MAX(created_at)
FROM creations
WHERE ....
GROUP BY user_id
ORDER BY created_at DESC

これにより、各user_idの最新のcreated_atが返されます。上位20のみが必要な場合は、追加します。

LIMIT 20

編集:これは基本的にUnreasonが上で言ったのと同じことです...どの行からデータが必要かを集計によって定義します。

4
Matthew

GROUP BYは、グループ化された列の個別の値を保証する必要があります。これにより、目的の結果が得られる場合があります。

(私はPostgreSQLではなく、MySQLとOracleに精通していませんが、2セントを投入していることに注意してください)

MySqlで

SELECT user_id, created_at
FROM creations
GROUP BY user_id
ORDER BY user_id

Oraclesqlplusの場合

SELECT user_id, FIRST(created_at)
FROM creations
GROUP BY user_id
ORDER BY user_id

これらにより、user_idに続いて、そのcreated_atに関連付けられたfirstuser_idが表示されます。別のcreated_atが必要な場合は、FIRSTをAVGMINMAX、またはLASTなどの他の関数に置き換えるオプションがあります。 Oracleの場合、他の列(返されない列を含む)にORDER BYを追加して、別のcreated_atを取得することもできます。

3
davur

サブクエリの使用は、irc#postgresqlチャネルの誰かによって提案されました。出来た:

SELECT user_id  
FROM (SELECT DISTINCT ON (user_id) * FROM creations) ss  
ORDER BY created_at DESC  
LIMIT 20;
2
mindtonic