SQL使用OVER和PARTITION BY

时间:2017-01-31 11:19:29

标签: sql sql-server sql-server-2012

我有下表;

ClientID    | Location  | Episode   | Date  
001         | Area1     | 4         | 01Dec16  
001         | Area2     | 3         | 01Nov16  
001         | Area2     | 2         | 01Oct16  
001         | Area1     | 1         | 01Sep16  
002         | Area2     | 3         | 21Dec16  
002         | Area1     | 2         | 21Nov16  
002         | Area1     | 1         | 21Oct16    

我希望根据客户端的最新一集创建2个新列

ClientID    | Location  | Episode   | Date  | LatestEpisode     | LatestLocation   
001         | Area1     | 4         | Dec   | 4                 | Area1  
001         | Area2     | 3         | Nov   | 4                 | Area1   
001         | Area2     | 2         | Oct   | 4                 | Area1  
001         | Area1     | 1         | Sep   | 4                 | Area1  
002         | Area2     | 3         | Dec   | 3                 | Area2  
002         | Area1     | 2         | Nov   | 3                 | Area2  
002         | Area1     | 1         | Oct   | 3                 | Area2      

我已经解决了我可以使用OVER来制作LatestEspisode: LatestEpisode = MAX(Episode) OVER(PARTITION BY ClientID)

但是无法弄清楚如何获取LatestLocation?

编辑:对不起,如果我没有正确的格式,这是我的第一篇文章。我试图看看如何正确发布,但我发现它很混乱

我在过去3天内多次搜索stackoverflow,并使用OVERROW NUMBER()找到了各种方法,但我对它们没有太多经验。我之前发现的许多示例都适用于生成聚合表,但我想保留完整的表,这就是为什么我认为使用OVER是可行的方法。

4 个答案:

答案 0 :(得分:2)

Sql server 2012版本引入了FIRST_VALUE()函数, 这使您可以像这样编写选择查询:

SELECT  ClientID, 
        Location, 
        Episode, 
        [Date], 
        LatestEpisode = FIRST_VALUE(Episode) OVER(PARTITION BY ClientID ORDER BY [Date] DESC), 
        LatestLocation = FIRST_VALUE(Location) OVER(PARTITION BY ClientID ORDER BY [Date] DESC) 
FROM tableName

答案 1 :(得分:1)

在SQL Server中,我会使用cross apply执行此操作:

select e.*, e2.episode as LatestEpisode, e2.location as LatestLocation
from episodes e cross apply
     (select top 1 e2.*
      from episodes e2
      where e2.clientId = e.clientId
      order by e2.episode desc
     ) elast;

尽管您可以使用窗口函数表达此逻辑,但横向连接(使用apply关键字在SQL Server中实现)是表达逻辑的更自然的方式。

如果您不熟悉横向连接,可以将它们视为from子句中的相关子查询。 。 。但是允许您返回多个列的查询。不过,我应该补充说,其中一个主要用例是表值函数,因此它是一个非常强大的构造。

答案 2 :(得分:0)

首先,您需要为每个客户选择LatestEpisode,然后您可以使用此值来标识行,您可以从中获取LatestLocation

SELECT *
    ,(
        SELECT Location
        FROM Episodes
        WHERE ClientId = MyTable.ClientId
            AND Episode = MyTable.LatestEpisode
        ) AS LatestLocation
FROM (
    SELECT *
        ,MAX(Episode) OVER (PARTITION BY ClientId) AS LatestEpisode
    FROM Episodes
    ) AS MyTable

您还可以使用公用表表达式(CTE):

WITH cte
AS (
    SELECT *
        ,MAX(Episode) OVER (PARTITION BY ClientId) AS LatestEpisode
    FROM Episodes
    )
SELECT cte.*
    ,(
        SELECT Location
        FROM Episodes
        WHERE ClientId = cte.ClientId
            AND Episode = cte.LatestEpisode
        ) AS LatestLocation
FROM cte

答案 3 :(得分:0)

我已经研究过并且能够产生所需的结果 请尝试以下

Declare @Table table ( ClientID varchar(max), Location varchar(500), Episode int, Dated varchar(30)) 

Insert Into @Table 
Values ('001', 'Area1', 4 ,'01Dec16' )
,('001', 'Area2', 3, '01Nov16')
, ('001', 'Area2', 2, '01Oct16')  
,('001' ,'Area1' ,1, '01Sep16')
,('002' ,'Area2' ,3, '21Dec16') 
,('002' ,'Area1' ,2, '21Nov16') 
,('002' ,'Area1' ,1, '21Oct16') 


; WITH LL AS 
(
SELECT CLientID ,MAX(CAST (Dated as Date)) as maxdate
FROM @table 
GROUP BY ClientID
) 
, Area AS 
(
SELECT Location, x.ClientID, x.Dated FROM @Table x INNER JOIN LL b ON x.ClientID = b.ClientID AND x.Dated = b.maxdate
) 
SELECT a.*
, LatestEpisode = MAX(Episode) OVER(PARTITION BY a.ClientID)
, LatestLocation =  b.Location
FROM @Table a 
INNER JOIN Area b ON a.ClientID = b.ClientID