SQL 窗口函数 - SELECT DISTINCT ORDER BY LIMIT

Posted

技术标签:

【中文标题】SQL 窗口函数 - SELECT DISTINCT ORDER BY LIMIT【英文标题】:SQL Window Functions - SELECT DISTINCT ORDER BY LIMIT 【发布时间】:2013-04-19 19:27:58 【问题描述】:

我的 PostgreSQL 数据库中有这 3 个表:

艺术家: id、姓名 专辑: ID、标题、年份、艺术家 ID 歌曲: id、标题、album_id

基本上每个艺人都有多张专辑,每张专辑都有多首歌曲。

我的查询正确返回了 25 个不同的艺术家 ID,他们的歌曲标题以“The”开头,按专辑年份排序:

SELECT id
FROM (

    -- Remove the duplicate artists
    SELECT DISTINCT ON (a.id) id, row
    FROM (

        -- Get all matching artists 
        -- This is slow because there is no limit
        SELECT
            artist.id,
            row_number() OVER(ORDER BY album.year DESC) as row
        FROM artist
        LEFT JOIN album ON album.artist_id = artist.id
        LEFT JOIN song ON song.album_id = album.id
        WHERE song.title ilike 'The %'
        ORDER BY album.year DESC

    ) as a

) as b
ORDER BY row
LIMIT 25

但是它速度慢且效率低,因为最里面的查询没有 LIMIT,因此它将搜索整个表以查找所有可能的匹配项。理想情况下,当找到 25 个不同的艺术家 ID 时,它会停止搜索。

可以重写或优化此查询以更快地执行吗?

我认为window functions 可以在这里加快速度,但我一直无法找出可行的方法。

谢谢!

【问题讨论】:

你看过查询的计划了吗?可以展示一下吗? 如果你在歌曲表中使用过滤器,你可以在这个关系中使用内连接,不是吗? 请阅读***.com/tags/postgresql-performance/info 并相应地更新您的帖子。 pg版本、查询计划等 【参考方案1】:
select id, year
from (
    SELECT DISTINCT ON (artist.id) artist.id, album.year
    FROM artist
    inner JOIN album ON album.artist_id = artist.id
    inner JOIN song ON song.album_id = album.id
    WHERE song.title ilike 'The %'
    ORDER BY artist.id, album.year DESC
) s
order by year desc
LIMIT 25

当 ilike 表达式确实以 % 开头时,song.title 上的索引会有所帮助

【讨论】:

这个答案的另一个好处是外连接仍然是外连接。我以前没有注意到,但是,在最初的问题中,由于在 where 子句中引用了外连接表,它实际上变成了内连接。 此查询不会返回25 distinct artist.id's who have a song whose title begins with "The "。它总是返回前 25 个艺术家 ID。 这需要 ORDER BY album.year -- 而不是 artist.id,因此是我的嵌套选择语句。子查询左连接很有趣,我会看看。 谢谢,这是一个重大改进。比我的查询快 27%。但我接受@Akash 的回答,因为它是一个单一的 SELECT 并且速度更快。【参考方案2】:

试试这个,应该比你当前的查询更快

SELECT
  artist.id,                        
  MAX( album.year ) as latest_album_date
FROM 
  artist
  JOIN album ON album.artist_id = artist.id  -- JOIN, not LEFT JOIN
  JOIN song ON song.album_id = album.id      -- Since you have song.title in WHERE clause, it makes no sense to make these as a LEFT JOIN
WHERE 
  song.title ilike 'The %'
GROUP BY
  artist.id
ORDER BY
  latest_album_date DESC
limit 25;

SQLFIDDLE

【讨论】:

group bydistinct 应该足够了。在此查询中无需同时使用两者。 group by 也将使用索引来删除重复项。 谢谢,这就是我一直在寻找的,现在看起来很明显。平均查询时间缩短 32%!【参考方案3】:

试试这个,

Select id, year
from (
    SELECT DISTINCT ON (artist.id) artist.id, album.year
    FROM artist
    rightJOIN album ON album.artist_id = artist.id
    left JOIN song ON song.album_id = album.id
    WHERE song.title ilike 'The %'
    ORDER BY artist.id, album.year DESC
) s
order by year desc
LIMIT 25

【讨论】:

以上是关于SQL 窗口函数 - SELECT DISTINCT ORDER BY LIMIT的主要内容,如果未能解决你的问题,请参考以下文章

sql查询语句并不是最先执行SELECT

Sql server - 窗口函数只能出现在 SELECT 或 ORDER BY 子句中

SQL 窗口函数 - SELECT DISTINCT ORDER BY LIMIT

从 SQL SELECT 中的子查询和 ROW_NUMBER 窗口函数生成“平均”列

面试官:SQL 查询总是先执行SELECT语句吗?你们都错了!

MySQL5.7实现ROW_NUMBER窗口函数功能SQL