TSQL窗口函数最佳做法

时间:2018-08-13 17:15:47

标签: sql-server tsql window-functions

我在日常查询中越来越多地使用窗口函数,并且一直想知道自己是否正确执行了操作。

假设我们有一个数据库dbo.songs,其中每首歌曲包含一个记录,并包含以下列:artistsongNamereleaseDate
对于每个艺术家,我想选择他们的第一个songNamereleaseDate,按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

这可以完成工作,但是效率似乎并不高-特别是如果我们在releaseDateartist上没有索引。如果一位艺术家每天发行两首歌曲,我们也会遇到问题。 此外,如果我们要进行一些时髦的优先级排序(可能的话,请选择2018年1月1日发行的歌曲,否则请选择最早发行的歌曲),我们几乎无法像使用窗口函数那样轻松地做到这一点:{{ 1}},这有点古怪,但简洁。

我们还有其他选择:使用OVER (PARTITION BY s.artist ORDER BY IIF(s.releaseDate = '20180101', '19000101', s,releaseDate))的self-CROSS APPLY,但据我所知,它们的效率或简明程度都比“绑定相同范围的窗口函数”上面概述的策略。

所以,我的问题是:最佳实践是什么?您将如何处理这个问题,既节省处理器周期,又避免代码库长度加倍?一个选项在CTE内部是否更好,而另一个选项在插入临时表中更好?

任何与现有标准,论文或资源的链接都将受到赞赏。

2 个答案:

答案 0 :(得分:4)

1)您应该首先获得与众不同的艺术家。如果您已有艺术家表,则从中选择。如果您不这样做,则创建一个歌手表,并用一个外键使歌曲表与之相关。

2)完成此操作后,CROSS APPLY将是检索相关歌曲数据的合适运算符。

SELECT a.artist, t.songName, t.releaseDate
FROM artists a
CROSS APPLY (
    SELECT TOP 1 s.songName, s.releaseDate
    FROM songs s
    WHERE s.artistId = a.artistId
    -- any other "funky" prioritization.
    ORDER BY s.releaseDate ASC
) topSongs t

答案 1 :(得分:0)

您可以使用pip3 install keras 分析函数:

subquery