Sql server TSQL窗口函数最佳实践

Sql server TSQL窗口函数最佳实践,sql-server,tsql,window-functions,Sql Server,Tsql,Window Functions,我在日常查询中越来越多地使用窗口函数,一直在想我是否做得对 假设我们有一个数据库dbo.songs,其中每首歌包含一条记录,包含以下列:艺术家、歌曲名和发布日期。 对于每个艺术家,我想选择他们的第一首歌名和releaseDate,按releaseDate升序排列。请注意,对艺术家进行分组的决定是任意的——明天,我可能需要按不同的列BPM、album、length进行分组 为此,我们有几个选择: 最近,我一直在使用一组范围相同的窗口函数策略,它看起来像这样: SELECT DISTINCT

我在日常查询中越来越多地使用窗口函数,一直在想我是否做得对

假设我们有一个数据库dbo.songs,其中每首歌包含一条记录,包含以下列:艺术家、歌曲名和发布日期。 对于每个艺术家,我想选择他们的第一首歌名和releaseDate,按releaseDate升序排列。请注意,对艺术家进行分组的决定是任意的——明天,我可能需要按不同的列BPM、album、length进行分组

为此,我们有几个选择:

最近,我一直在使用一组范围相同的窗口函数策略,它看起来像这样:

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
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
这看起来有点马虎,不是吗?它完全依赖于DISTINCT来避免一百万个重复行,如果您想选择其他字段BPM、album、length,则需要更多的窗口函数,我相信这些函数可以算作RBAR

选项二是找出键,然后连接到self,如下所示:

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
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
这就完成了任务,但似乎效率不高——特别是如果我们没有发布日期和艺术家的索引的话。如果一位艺术家在一天内发布了两首歌曲,我们也会遇到问题。 此外,如果我们正在做一些时髦的优先排序,如果可能的话,选择2018-01-01的歌曲发布,否则最早发布的歌曲,我们不能像使用窗口功能一样轻松地完成:按s.Artister ORDER按IIFs.releaseDate进行过分割。releaseDate='20180101','19000101',s,releaseDate,这有点粗糙,但简洁

我们还有其他选择:自交叉应用,使用ROW_NUMBER,但从我所能看出,这些方法要么效率较低,要么不如上面概述的一组范围相同的窗口函数策略简洁

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

非常感谢现有标准、论文或资源的链接。

您可以使用子查询或分析功能:

select s.*
from dbo.songs s
where releaseDate = (select min(s1.releaseDate)
                     from dbo.songs s1
                     where s.artist = s1.artist
                    );

你应该先找到你独特的艺术家。如果您已经有艺术家表,请从中选择。如果没有,则创建一个艺术家表,并使用外键将歌曲表与之关联

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

在本例中,您没有要加入的艺术家表吗?这样就不需要进行区分。如果没有,请创建一个。@DanielGimenez按艺术家分组的选择是任意的-如果我有一个名为songLength的列,并希望按该列分组,那么让一个表包含所有可能的歌曲长度可能是没有意义的。我将对我的问题进行编辑,以澄清分组在上的列不一定是一个好键。鉴于必须对每一行执行查询,这在一个大表上不是会花费很多钱吗?我的理解正确吗?@Cowthulhu从我的经验来看,是的,这是非常低效的。我做了一些研究——看起来我关于交叉申请低效的假设不一定准确。非常好的选择,谢谢。