【发布时间】:2019-01-20 11:41:55
【问题描述】:
我在日常查询中越来越多地使用窗口函数,并且一直想知道我是否正确地使用它。
假设我们有一个数据库dbo.songs,其中包含每首歌曲的一条记录,具有以下列:artist、songName 和 releaseDate。
对于每个艺术家,我想选择他们的第一个songName 和releaseDate,按releaseDate 升序排列。请注意,在artist 上分组的决定是任意的 - 明天,我可能需要按不同的列(BPM、专辑、长度)分组。
为此,我们有几个选择:
最近,我一直在使用“一堆相同范围的窗口函数”策略,看起来像这样:
SELECT DISTINCT
s.artist
, FIRST_VALUE(s.songName) OVER (PARTITION BY s.artist ORDER BY s.releaseDate ASC) AS songName
, FIRST_VALUE(s.releaseDate) OVER (PARTITION BY s.artist ORDER BY s.releaseDate ASC) AS releaseDate
FROM dbo.songs s
这似乎有点草率,不是吗?它完全依赖DISTINCT 来避免一百万行重复,如果你想选择额外的字段(BPM、专辑、长度),你需要更多的窗口函数,我相信这会算作 RBAR。
选项二是“找出键然后加入自我”,如下所示:
WITH earliestArtistRelease AS (
SELECT
s.artist
, MIN(s.releaseDate) AS releaseDate
FROM dbo.songs s
GROUP BY s.artist
)
SELECT
e.artist
, e.releaseDate
, s.songName
FROM dbo.songs s
INNER JOIN earliestArtistRelease e
ON s.releaseDate = e.releaseDate
AND s.artist = e.artist
这样就完成了工作,但它似乎并没有那么高效 - 特别是如果我们没有在 releaseDate 和 artist 上的索引。如果一位艺术家在一天内发布了两首歌曲,我们也会遇到问题。
此外,如果我们正在做一些时髦的优先级排序(如果可能,选择 2018 年 1 月 1 日发布的歌曲,否则选择最早发布的歌曲),我们不能像使用窗口函数那样简单:@987654334 @,有点老套,但简洁。
我们还有其他选择:self-CROSS APPLY,使用ROW_NUMBER(),但据我所知,这些往往比上面概述的“一堆相同范围的窗口函数”策略效率低或简洁。
那么,我的问题是:最佳做法是什么?您将如何处理这个问题,既可以节省处理器周期,又可以避免代码库的长度加倍?一个选项在 CTE 中更好,而另一个更适合插入到临时表中?
非常感谢任何指向现有标准、论文或资源的链接。
【问题讨论】:
-
在此示例中您没有要加入的
artist表?这将消除对不同的需求。如果没有,请创建一个。 -
@DanielGimenez 选择按
artist分组是任意的-如果我有一个名为songLength的列并想按此分组,那么拥有一个包含每个可能的歌曲长度。我将编辑我的问题以澄清分组所在的列可能不一定是一个好键。
标签: sql-server tsql window-functions