SQL Server 分区窗口中多个属性的 Min() 和 Max()
Posted
技术标签:
【中文标题】SQL Server 分区窗口中多个属性的 Min() 和 Max()【英文标题】:Min() and Max() of multiple attributes in a partition window on SQL Server 【发布时间】:2020-09-28 15:22:28 【问题描述】:我在 SQL Server 中有一个时间表,其中包含公共交通工具的 [SERV_ID]
(服务 ID)、[STATION]
(车站)、[ARR]
(到达时间)、[DEP]
(出发时间)。每个服务都可以每天都在[SERV_DAY]
。
目标是总结Serviceday、Service-line、First-station、Last-station以及相应的时间戳。 --> 每天每服务一行。
对于[SERV_ID]
N170,这将是:
SERV_DAY SERV_ID FIRST_STATION MIN_DEP LAST_STATION MAX_ARR
2019-08-14 00:00:00 N170 Downtown 2019-08-14 06:06:00 CentralStation 2019-08-14 07:11:00
我尝试通过([SERV_DAY], [SERV_ID])
进行分区,然后为每个分区获取MAX([ARR])
和MIN([DEP])
。这工作了很长时间,但现在我想为每个最小值和最大值获取对应的 Station。
SELECT
[SERV_DAY],[SERV_ID],
MAX([ARR]) OVER(PARTITION BY [SERV_DAY],[SERV_ID]) AS MAX_ARR,
MIN([DEP]) OVER(PARTITION BY [SERV_DAY],[SERV_ID]) AS MIN_DEP
FROM #demo
稍后我需要在最后一站添加延迟,该延迟在数据集的扩展版本中可用,为 [ARR_EFFECTIVE]
和 [DEP_EFFECTIVE]
。希望一旦我知道如何总结如上所述的每日线路,我就能够添加这些属性。
这个话题很接近,但我不明白如何适应“差距和孤岛问题” Min() and Max() based on partition in sql server
我在 dbfiddle 中设置了一个演示数据集 https://dbfiddle.uk/?rdbms=sqlserver_2016&fiddle=52e53d43a49ddb8f67454e576bfa7d74
谁能帮我完成查询?
【问题讨论】:
什么版本的 SQL Server? 我使用 Microsoft SQL Server 2017 【参考方案1】:SELECT
[SERV_DAY]
,[SERV_ID],
FIRST_VALUE(STATION) over (Partition by [SERV_DAY],[SERV_ID] Order by ARR DESC) Station1
, FIRST_VALUE(STATION) over (Partition by [SERV_DAY],[SERV_ID] Order by DEP ASC) Station2
FROM #demo
【讨论】:
注意那个技巧。因为当获取多于一列的first_value
时,这些值可能来自不同的行。因此,一列与其他列不匹配。如果不止一行提供与订单相同的排名(此处为ARR
和DEP
),则可能会发生这种情况。这也是我使用 row_number 而不是 dense_rank 进行过滤的原因。
@casenonsensitive 我按 Datetime 日期类型(Arr 和 Dep)排序,并且根据场景,我们不能有相同的到达时间(ARR)或出发时间(Dep)一天,所以这里不是这种情况。【参考方案2】:
如果您有大量数据,我想我会使用临时表而不是 CTE,但这里有一个关于它应该如何工作的快速想法:
WITH CTE AS
(
SELECT *
, ROW_NUMBER() OVER(PARTITION BY [SERV_DAY],[SERV_ID] ORDER BY ARR ) RN
, ROW_NUMBER() OVER(PARTITION BY [SERV_DAY],[SERV_ID] ORDER BY DEP ) RN2
from #demo
)
SELECT t1.[SERV_DAY],t1.[SERV_ID],t1.[STATION] FIRST_STATION, t1.[DEP] MIN_DEP, t2.STATION LAST_STATION
FROM CTE t1
INNER JOIN CTE t2 on t1.SERV_DAY = t2.SERV_DAY and t1.SERV_ID = t2.SERV_ID and t2.RN2 = 1
WHERE t1.RN = 1
【讨论】:
谢谢!这似乎真的很完美,因为这个查询很容易扩展到有效到达、离开,以便稍后计算延迟! 太棒了,如果它满足您的需求,请将其标记为您接受的答案。 对不起,我不能在这个糟糕的平台上标记答案;感谢您的反馈!声望低于 15 人的投票会被记录,但不会更改公开显示的帖子分数【参考方案3】:您可以分两步完成:
首先添加一个按ARR
降序排序的row_number 和另一个按dep
排序的row_number。然后,您可以过滤 row_number
= 1 的行以选择其他列。
下面是一个如何检索 max_arr 和 min_dep 的站的示例:
WITH T AS (
SELECT
[SERV_DAY], [SERV_ID],
MAX([ARR]) OVER(PARTITION BY [SERV_DAY],[SERV_ID]) AS MAX_ARR,
MIN([DEP]) OVER(PARTITION BY [SERV_DAY],[SERV_ID]) AS MIN_DEP,
ROW_NUMBER() OVER(PARTITION BY [SERV_DAY],[SERV_ID] ORDER BY [ARR] DESC) AS RN_ARR,
ROW_NUMBER() OVER(PARTITION BY [SERV_DAY],[SERV_ID] ORDER BY [DEP]) AS RN_DEP,
*
FROM #demo
)
SELECT MAX(CASE WHEN RN_ARR = 1 THEN [STATION] END) MAX_ARR_STATION,
MAX(CASE WHEN RN_DEP = 1 THEN [STATION] END) MIN_DEP_STATION,
*
FROM T
【讨论】:
谢谢!不幸的是,@SELECT [SERV_DAY], [SERV_ID], MAX([ARR]) OVER(PARTITION BY [SERV_DAY],[SERV_ID]) AS MAX_ARR, MIN([DEP]) OVER(PARTITION BY [SERV_DAY],[SERV_ID]) AS MIN_DEP, ROW_NUMBER() OVER(PARTITION BY [SERV_DAY],[SERV_ID] ORDER BY [ARR] DESC) AS RN_ARR, ROW_NUMBER() OVER(PARTITION BY [SERV_DAY],[SERV_ID] ORDER BY [DEP]) AS RN_DEP, * FROM #demo
不适合在 temptable 中或与 [SERV_DAY], [SERV_ID]
一样作为重复列。有没有办法单独重命名一个版本?
@Samamani 而不是*
,只需写下您需要选择的所有列。这样你就可以确保没有列名被使用两次。【参考方案4】:
作为对@casenonsensitive 的回复,它可以使用他的代码并稍作修改!
WITH T AS (
SELECT
[SERV_DAY], [SERV_ID], [STATION],
MAX([ARR]) OVER(PARTITION BY [SERV_DAY],[SERV_ID]) AS MAX_ARR,
MIN([DEP]) OVER(PARTITION BY [SERV_DAY],[SERV_ID]) AS MIN_DEP,
ROW_NUMBER() OVER(PARTITION BY [SERV_DAY],[SERV_ID] ORDER BY [ARR] ) AS RN_ARR,
ROW_NUMBER() OVER(PARTITION BY [SERV_DAY],[SERV_ID] ORDER BY [DEP] ) AS RN_DEP
FROM #demo
)
SELECT MAX(CASE WHEN RN_ARR = 1 THEN [STATION] END) MIN_DEP_STATION,
MAX(CASE WHEN RN_DEP = 1 THEN [STATION] END) MAX_ARR_STATION, [SERV_DAY], [SERV_ID], MAX_ARR, MIN_DEP from T
group by [SERV_DAY], [SERV_ID], MIN_DEP, MAX_ARR
【讨论】:
以上是关于SQL Server 分区窗口中多个属性的 Min() 和 Max()的主要内容,如果未能解决你的问题,请参考以下文章