4

PostgreSQL 9.1 (ホスティング プラットフォームがアップグレードされるとすぐに 9.2) を使用して、次のクエリを実行します。

SELECT
    media_files.album,
    media_files.artist,
    ARRAY_AGG (media_files. ID) AS media_file_ids
FROM
    media_files
INNER JOIN playlist_media_files ON media_files.id = playlist_media_files.media_file_id
WHERE
    playlist_media_files.playlist_id = 1
GROUP BY
    media_files.album,
    media_files.artist
ORDER BY
    media_files.album ASC

目標は、アルバム/アーティストの組み合わせを抽出し、結果セットにその特定の組み合わせのメディア ファイル ID の配列を含めることでした。

問題は、メディア ファイルに別の列があることですartwork

artworkは (同じアルバム内であっても) メディア ファイルごとに一意ですが、結果セットではセットの最初のものだけを返す必要があります。

したがって、10 個のメディア ファイルを含むアルバムの場合、対応するアートワークも 10 個ありますが、最初のもの (またはそのコレクション用にランダムに選択されたもの) だけを返したいと思います。

SQL/ウィンドウ関数 (first_value over..) だけでそれを行うことは可能ですか?

4

2 に答える 2

4

はい、可能です。まず、エイリアスと明示的な列修飾子を追加してクエリを微調整して、何がどこから来るのかを明確にしましょう。テーブル定義がないと確信が持てないため、正しく推測したと仮定します。

SELECT
    mf.album,
    mf.artist,
    ARRAY_AGG (mf.id) AS media_file_ids
FROM
    "media_files" mf
INNER JOIN "playlist_media_files" pmf ON mf.id = pmf.media_file_id
WHERE
    pmf.playlist_id = 1
GROUP BY
    mf.album,
    mf.artist
ORDER BY
    mf.album ASC

SELECTこれで、リストでサブクエリを使用するか、 を使用することができますが、にDISTINCT ON基づくソリューションDISTINCT ONは非常に複雑になり、価値がないように見えます。

本当に必要なのはpick_arbitrary_value_agg、最初に見た値だけを選択して残りを捨てる集計のようなものです。そのような集約はなく、ジョブに実装する価値はありません。min(artwork)orを使用することもできmax(artwork)ますが、これは実際には後のソリューションよりも優れたパフォーマンスを発揮することがわかります。

サブクエリを使用するには、そのままにして、リストORDER BYに追加の列として次を追加します。SELECT

(SELECT mf2.artwork 
 FROM media_files mf2 
 WHERE mf2.artist = mf.artist
   AND mf2.album = mf.album
 LIMIT 1) AS picked_artwork

ORDER BY random()上記の前に追加することで、選択したアートワークをランダム化できますが、パフォーマンスが低下しますLIMIT 1

別の方法として、インラインでランダムな行の選択を実装する手っ取り早い方法を次に示します。

(array_agg(artwork))[width_bucket(random(),0,1,count(artwork)::integer)] 

サンプル データがないため、これらの変更をテストすることはできません。問題がある場合はお知らせください。

于 2012-12-13T05:41:01.293 に答える
3

「最初の」ピック

単に使用する方が簡単/安価ではないでしょうかmin():

SELECT m.album
      ,m.artist
      ,array_agg(m.id) AS media_file_ids
      ,min(m.artwork)  AS artwork
FROM   playlist_media_files p
JOIN   media_files          m ON m.id = p.media_file_id
WHERE  p.playlist_id = 1
GROUP  BY m.album, m.artist
ORDER  BY m.album, m.artist;

恣意的/ランダムピック

ランダムな選択を探している場合は、 @Craigが真にランダムな選択を行うソリューションを既に提供しています。

CTEを使用して、(おそらく大きい) ベース テーブルでの追加のスキャンを回避し、小さな結果セットで 2 つの別個の (安価な) サブクエリを実行することもできます。

任意の選択 - 完全にランダムではない場合、結果はテーブル内の行の物理的な順序と実装固有に依存します。

WITH x AS (
   SELECT m.album, m.artist, m.id, m.artwork
   FROM   playlist_media_files p
   JOIN   media_files          m ON m.id = p.media_file_id
   )
SELECT a.album, a.artist, a.media_file_ids, b.artwork
FROM  (
   SELECT album, artist, array_agg(id) AS media_file_ids
   FROM   x
   ) a
JOIN  (
   SELECT DISTINCT ON (1,2)  album, artist, artwork
   FROM x
   ) b USING (album, artist);

本当にランダムなORDER BY .. random()結果を得るには、次のようなものを subquery に追加できbます。

JOIN  (
   SELECT DISTINCT ON (1, 2)  album, artist, artwork
   FROM   x
   ORDER  BY 1, 2, random()
   ) b USING (album, artist);
于 2012-12-13T06:17:21.220 に答える