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)ormax(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来避免对(可能很大的)基表进行额外的扫描,然后在较小的结果集上运行两个单独的(便宜的)子查询。

对于任意选择 - 不是真正随机的,结果将取决于表中行的物理顺序和实现细节:

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()这样的添加到子查询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 回答