【问题标题】:FIRST_VALUE working correctly but LAST_VALUE not giving desired resultsFIRST_VALUE 工作正常,但 LAST_VALUE 没有给出想要的结果
【发布时间】:2021-09-08 19:15:55
【问题描述】:

在以下查询中:

SELECT 
    DISTINCT *,
    ROW_NUMBER() OVER (PARTITION BY dog_guid ORDER BY created_at ASC) AS test_number,
    LAG(created_at) OVER (PARTITION BY dog_guid ORDER BY created_at ASC) AS previous_test_date,
    CAST(DATEDIFF(SECOND, LAG(created_at) OVER (PARTITION BY dog_guid ORDER BY created_at ASC), created_at)/(60*60*24) AS FLOAT) AS date_diff_days,
    CAST(DATEDIFF(SECOND, LAG(created_at) OVER (PARTITION BY dog_guid ORDER BY created_at ASC), created_at)/(60) AS FLOAT) AS date_diff_mins,
    FIRST_VALUE(created_at) OVER (PARTITION BY dog_guid ORDER BY created_at ASC) AS first_test_date,
    LAST_VALUE(created_at) OVER (PARTITION BY dog_guid ORDER BY created_at ASC) AS last_test_date
FROM    
    complete_tests c
WHERE
    dog_guid IS NOT NULL

FIRST_VALUE() 函数工作正常,但 LAST_VALUE() 只是给出该列的日期。这是为什么呢?

【问题讨论】:

  • 因为您忘记了LAST_VALUE 中的范围。它的工作方式与文档中的完全一致(并且是正确的)。请参阅解释此确切行为的文档中的 example
  • 因为默认范围是BETWEEN RANGE UNBOUNDED PRECEDING AND CURRENT ROW,它适用于第一个值但不适用于最后一个值。您可以显式覆盖此范围,但更简单的方法是在相同的订单上执行 FIRST_VALUE(),但 DESC

标签: sql-server window-functions


【解决方案1】:

@JeroenMostert 所述,当您将ORDER BY 添加到OVER 子句(采用ROWS/RANGE 的分析函数)时,默认窗口为RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,因此窗口中的最后一个值始终是当前行。

你想要的是ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING,所以你需要明确地添加它。不直观,但就是这样。

出于性能原因,您还应该将此添加到FIRST_VALUE

SELECT 
    DISTINCT *,
    ROW_NUMBER() OVER (PARTITION BY dog_guid ORDER BY created_at ASC) AS test_number,
    LAG(created_at) OVER (PARTITION BY dog_guid ORDER BY created_at ASC) AS previous_test_date,
    CAST(DATEDIFF(SECOND, LAG(created_at) OVER (PARTITION BY dog_guid ORDER BY created_at ASC), created_at)/(60*60*24) AS FLOAT) AS date_diff_days,
    CAST(DATEDIFF(SECOND, LAG(created_at) OVER (PARTITION BY dog_guid ORDER BY created_at ASC), created_at)/(60) AS FLOAT) AS date_diff_mins,
    FIRST_VALUE(created_at) OVER (PARTITION BY dog_guid ORDER BY created_at ASC ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS first_test_date,
    LAST_VALUE(created_at) OVER (PARTITION BY dog_guid ORDER BY created_at ASC ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS last_test_date
FROM    
    complete_tests c
WHERE
    dog_guid IS NOT NULL

严格来说,您可以将其更改为FIRST_VALUEDESC,但这会影响性能,首先因为它需要第二次排序,其次因为窗口仍然是RANGE,这需要磁盘工作台。

【讨论】:

    猜你喜欢
    • 2013-03-01
    • 2021-08-29
    • 1970-01-01
    • 2013-10-26
    • 2021-03-02
    • 2017-08-25
    • 2021-12-10
    • 2018-10-17
    • 2019-11-27
    相关资源
    最近更新 更多