大日期范围内的最小/最大日期值取决于值

Posted

技术标签:

【中文标题】大日期范围内的最小/最大日期值取决于值【英文标题】:Min/Max Date Values over Large Date Range depending on Value 【发布时间】:2019-07-10 13:38:14 【问题描述】:

我正在查询客户数据的快照,其中包含快照日期、客户 ID 和该客户当天的“价值”。我使用 LAG 函数返回前几天的值,以了解是否有下降/上升/完全损失/完全新值(从 £0 到 > £0)。

最终的游戏是确定客户价值为 0 英镑的最小和最大日期。

最初我尝试按客户和价值进行 MIN(Date) 和 Max(Date) 分组。但是,如果客户在不同的日期范围内降至 0 英镑,它将恢复最新日期范围的最大值和最早日期范围的最小值,而不是理想的 - 将两个范围都恢复为 0 英镑。

我尝试使用 DENSE_RANK() 来拆分客户的每个值,但这样做只会将所有 £0 值排在同一排名中。

下面是一些示例代码,向您展示我正在使用的数据以及我如何尝试拆分它:

DROP TABLE IF EXISTS #SnapshotTable
CREATE TABLE #SnapshotTable
(
    Row_ID INT IDENTITY(1,1)
    ,SnapshotDate DATE
    ,SnapshotDateKey INT
    ,CustomerId INT
    ,Value DECIMAL(18,2)
)
INSERT INTO #SnapshotTable (SnapshotDate, SnapshotDateKey, CustomerId, Value)
SELECT '2019-01-01', 20190101, 1, 0.00
UNION SELECT '2019-01-02', 20190102, 1, 0.00
UNION SELECT '2019-01-03', 20190103, 1, 5.00
UNION SELECT '2019-01-04', 20190104, 1, 5.00
UNION SELECT '2019-01-05', 20190105, 1, 3.00
UNION SELECT '2019-01-06', 20190106, 1, 3.00
UNION SELECT '2019-01-07', 20190107, 1, 0.00
UNION SELECT '2019-01-08', 20190108, 1, 0.00
UNION SELECT '2019-01-09', 20190109, 1, 10.00
UNION SELECT '2019-01-10', 20190110, 1, 0.00

SELECT * FROM #SnapshotTable

-- Code that doesn't work correctly
SELECT
    CustomerId
    ,Value
    ,MinDate = MIN(SnapshotDateKey)
    ,MaxDate = MAX(SnapshotDateKey)
FROM #SnapshotTable
GROUP BY
    CustomerId
    ,Value

-- Attempted with dense rank
ALTER TABLE #SnapshotTable
ADD DenseRankTest INT NULL
GO
-- Update with Dense Rank
UPDATE TGT
SET 
    TGT.DenseRankTest = SRC.NewRank
FROM #SnapshotTable TGT
INNER JOIN (SELECT
                Row_ID
                ,NewRank = DENSE_RANK() OVER (PARTITION BY CustomerId ORDER BY Value ASC)
            FROM #SnapshotTable

            ) AS SRC
    ON SRC.Row_ID = TGT.Row_ID 

SELECT * FROM #SnapshotTable

现在我可以看到,dense_rank() 函数的功能是我想要的,但老实说,我已经研究了一段时间了,我无法弄清楚如何正确地做到这一点。

有人可以告诉我我需要做什么吗?

我期待看到:

SELECT [StartDateKey] = 20190101, [EndDateKey] = 20190102, [CustomerId] = 1, [Value] = 0
UNION SELECT [StartDateKey] = 20190103, [EndDateKey] = 20190104, [CustomerId] = 1, [Value] = 5
UNION SELECT [StartDateKey] = 20190105, [EndDateKey] = 20190106, [CustomerId] = 1, [Value] = 3
UNION SELECT [StartDateKey] = 20190107, [EndDateKey] = 20190108, [CustomerId] = 1, [Value] = 0
UNION SELECT [StartDateKey] = 20190109, [EndDateKey] = 20190109, [CustomerId] = 1, [Value] = 10
UNION SELECT [StartDateKey] = 20190120, [EndDateKey] = 20190110, [CustomerId] = 1, [Value] = 0

编辑:对于那些偶然发现这一点的人,在这里的人们的帮助下,我找到了this as a good read for understanding the issue/solving the issue.

【问题讨论】:

优秀的职位发布数据和详细信息。您的第一篇文章令人印象深刻,而且很多人都无法理解。这听起来像是一个空白和孤岛问题。围绕 SO 有很多关于解决此问题的方法的示例。 您的问题有一点令人困惑。您多次声明要查找值为 0 的行。但在您想要的结果中,您还将值为 5 的两个连续行分组。这与描述相矛盾。 @TabAlleman 所需的输出使这一点更加清晰。他们想要每组值的最小和最大日期。至少我是这么解释的。 @TabAlleman 抱歉,我应该更清楚一点。你是对的,这是所需的输出,但是我很感兴趣看到中间输出,然后我将过滤 WHERE Value = 0。谢谢。 @SeanLange 我以前从未听说过“差距和岛屿”,所以老实说,我不确定要在 Google/Stack 上搜索什么!感谢您的帮助,我会阅读的。 【参考方案1】:

这是一个孤岛问题。但是对所谓的重复项的公认答案根本不是解决这个问题的最佳方法。投票率更高的答案仍然过于复杂。

一个更简单的方法是:

select customerid, value, min(SnapshotDateKey), max(SnapshotDateKey)
from (select st.*,
             row_number() over (partition by customerid, value order by snapshotdate) as seqnum
      from snapshottable st
     ) st
group by dateadd(day, -seqnum, snapshotdate), customerid, value
order by min(SnapshotDateKey);

Here 是一个 dbfiddle。

【讨论】:

谢谢!这是完美的,我将阅读有关间隙和岛屿的内容,以防我将来可以帮助/遇到这个问题。使用 dateadd 做得很好,我没有想到这一点。再次感谢您!

以上是关于大日期范围内的最小/最大日期值取决于值的主要内容,如果未能解决你的问题,请参考以下文章

shell生成指定范围内的随机数

shell生成指定范围内的随机数

如何获取日期范围内的缺失值?

选择日期范围内的分组值总和(窗口函数)

子查询返回超过 1 个值 - 使用特定日期的值更新日期范围内的记录

SQL在where语句中使用日期范围的选择子查询来确定该日期范围内的最大值