sql – 日期范围的MIN或MAX值 – 根据产品ID,价格和日期范围确定给定日期范围的最低价格

我真的希望你们中的一些人喜欢挑战.我有一个产品ID,价格和日期范围的表格,当这些价格活跃时.

+----+-------+---------------------+---------------------+
| Id | Price |      StartDate      |       EndDate       |
+----+-------+---------------------+---------------------+
|  1 |    19 | 2016-12-01 00:00:00 | 2017-12-01 23:59:59 |
|  1 |    18 | 2017-01-01 00:00:00 | 2018-01-12 23:59:59 |
|  1 |    17 | 2017-02-03 00:00:00 | 2017-03-03 23:59:59 |
|  1 |    16 | 2018-01-01 00:00:00 | 2018-03-02 23:59:59 |
|  2 |    15 | 2017-01-01 00:00:00 | 2017-03-05 23:59:59 |
|  2 |    15 | 2017-03-06 00:00:00 | 2017-03-31 23:59:59 |
|  2 |    30 | 2017-04-01 00:00:00 | 2017-05-03 23:59:59 |
|  3 |    12 | 2017-01-01 00:00:00 | 2017-01-31 23:59:59 |
|  3 |    12 | 2017-02-01 00:00:00 | 2017-02-28 23:59:59 |
|  4 |    14 | 2017-01-01 00:00:00 | 2017-04-05 23:59:59 |
|  4 |    14 | 2017-04-01 00:00:00 | 2017-04-30 23:59:59 |
|  4 |    12 | 2017-04-15 00:00:00 | 2017-05-30 23:59:59 |
|  5 |    20 | 2017-01-01 00:00:00 | 2017-01-31 23:59:59 |
|  5 |    20 | 2017-03-01 00:00:00 | 2017-03-31 23:59:59 |
|  6 |    15 | 2017-01-01 00:00:00 | 2017-01-31 23:59:59 |
|  6 |    15 | 2017-02-01 00:00:00 | 2017-02-28 23:59:59 |
|  6 |    15 | 2017-04-01 00:00:00 | 2017-04-30 23:59:59 |
+----+-------+---------------------+---------------------+

SQLFiddle:http://sqlfiddle.com/#!6/39288/1

我需要以下列格式获取它:

>日期与“触摸”(即Id#3)合并为一个期间的ID和价格相同.
>重叠的日期(即Id#4)合并为一个期间.
>显示每种产品的最低价格以及在什么范围内.
>具有间隙和相同价格的日期范围不合并,并且是单独的行(即Id#5).

结果应该是:

+----+-------+---------------------+---------------------+
| Id | Price |      StartDate      |       EndDate       |
+----+-------+---------------------+---------------------+
|  1 |    19 | 2016-12-01 00:00:00 | 2016-12-31 23:59:59 |
|  1 |    18 | 2017-01-01 00:00:00 | 2017-02-02 23:59:59 |
|  1 |    17 | 2017-02-03 00:00:00 | 2017-03-03 23:59:59 |
|  1 |    19 | 2017-03-04 00:00:00 | 2017-12-01 23:59:59 |
|  1 |    18 | 2017-12-02 00:00:00 | 2017-12-31 23:59:59 |
|  1 |    16 | 2018-01-01 00:00:00 | 2018-03-02 23:59:59 |
|  2 |    15 | 2017-01-01 00:00:00 | 2017-03-31 23:59:59 |
|  2 |    30 | 2017-04-01 00:00:00 | 2017-05-03 23:59:59 |
|  3 |    12 | 2017-01-01 00:00:00 | 2017-02-28 23:59:59 |
|  4 |    14 | 2017-01-01 00:00:00 | 2017-04-14 23:59:59 |
|  4 |    12 | 2017-04-15 00:00:00 | 2017-05-30 23:59:59 |
|  5 |    20 | 2017-01-01 00:00:00 | 2017-01-31 23:59:59 |
|  5 |    20 | 2017-03-01 00:00:00 | 2017-03-31 23:59:59 |
|  6 |    15 | 2017-01-01 00:00:00 | 2017-02-28 23:59:59 |
|  6 |    15 | 2017-04-01 00:00:00 | 2017-04-30 23:59:59 |
+----+-------+---------------------+---------------------+

总的来说,它基本上决定了两个日期之间的最佳价格.

我过去曾使用过这个表,并且能够用C#解决它,但这次我需要一个纯粹的TSQL方法.

我已经放下了一些深度嵌套的CTE并且失去了理智,因为得到的结果远不及它们应该是什么.提前感谢能够提供帮助的任何人.

编辑:我甚至搞砸了想要的结果,因为这太令人困惑了.固定(我认为).

编辑2:示例:

+------+-------+-------------------------+-------------------------+
|  Id  | Price |        StartDate        |         EndDate         |
+------+-------+-------------------------+-------------------------+
| 8611 | 31.98 | 2017-06-06 00:00:00.000 | 2017-09-24 23:59:59.000 |
| 8611 | 31.98 | 2017-09-25 00:00:00.000 | 2017-12-31 23:59:59.000 |
| 8611 | 28.78 | 2017-07-31 00:00:00.000 | 2017-09-30 23:59:59.000 |
| 8611 | 28.78 | 2017-10-30 00:00:00.000 | 2017-12-31 23:59:59.000 |
+------+-------+-------------------------+-------------------------+

@ GordonLinoff的结果是:

+------+-------+-------------------------+-------------------------+
|  Id  | Price |        StartDate        |         EndDate         |
+------+-------+-------------------------+-------------------------+
| 8611 | 28.78 | 2017-06-06 00:00:00.000 | 2017-12-31 23:59:59.000 |
+------+-------+-------------------------+-------------------------+

结果应该是:

+------+-------+-------------------------+-------------------------+
|  Id  | Price |        StartDate        |         EndDate         |
+------+-------+-------------------------+-------------------------+
| 8611 | 31.98 | 2017-06-06 00:00:00.000 | 2017-07-30 23:59:59.000 |
| 8611 | 28.78 | 2017-07-31 00:00:00.000 | 2017-09-30 23:59:59.000 |
| 8611 | 31.98 | 2017-10-01 00:00:00.000 | 2017-10-29 23:59:59.000 |
| 8611 | 28.78 | 2017-10-30 00:00:00.000 | 2017-12-31 23:59:59.000 |
+------+-------+-------------------------+-------------------------+
最佳答案
你有日历/日期表吗?如果是这样,那么您可以使用日期表来帮助您获得表中期间内每个日期的每个产品的最低价格.

之后,您可以通过查看具有相同产品ID的下一个和上一个记录来获取每个期间的开始和结束日期.您可以使用LAG和LEAD功能执行此操作.这为您提供了每个所需组的外部边界.

从那里开始,只需稍微摆弄即可获得最终结果.我在下面提供了一个示例,它可以为您提供所需的结果.

--Get the best price per date for each product
WITH BestPricePerDate AS (
    SELECT 
        Id,
        MIN(Price) Price,
        c.[Date]
    FROM [YourTable] yt
        INNER JOIN dbo.Calendar c
            ON c.[Date] BETWEEN yt.StartDate AND yt.EndDate
    GROUP BY Id, [Date]
),
--Check whether the date is the start or the end of a period
PeriodsMarkedPerId AS(
    SELECT 
        Id,
        Price,
        [Date],
        CASE WHEN 
            ISNULL(LAG(Price,1) OVER (PARTITION BY Id ORDER BY [Date]),-1) <> Price 
            OR ISNULL(LAG([Date],1) OVER (PARTITION BY Id ORDER BY [Date]),'1999-01-01') <> DATEADD(DAY,-1,[Date]) THEN 1 ELSE 0 END IsStartDate,
        CASE WHEN 
            ISNULL(LEAD(Price,1) OVER (PARTITION BY Id ORDER BY [Date]),-1) <> Price 
            OR ISNULL(LEAD([Date],1) OVER (PARTITION BY Id ORDER BY [Date]),'1999-01-01') <> DATEADD(DAY,1,[Date]) THEN 1 ELSE 0 END IsEndDate
    FROM BestPricePerDate
),
--Keep only the start and end date records
PeriodStartAndEndDates AS(
    SELECT 
        Id, 
        Price,
        [Date],
        IsStartDate,
        IsEndDate
    FROM PeriodsMarkedPerId
    WHERE IsStartDate = 1 OR IsEndDate = 1
),
--Move StartDate and EndDate to one record
StartAndEndDatesOnSameRow AS(
    SELECT 
        Id, 
        Price, 
        [Date] AS StartDate,
        LEAD([Date],1) OVER (ORDER BY Id, [Date]) AS EndDate,
        IsStartDate
    FROM PeriodStartAndEndDates
)
--Get the resulting periods
SELECT Id, Price, StartDate, EndDate 
FROM StartAndEndDatesOnSameRow
WHERE IsStartDate = 1
ORDER BY Id, StartDate

如果您没有日期表,则可以轻松创建日期表.网上有很多这方面的例子.

我希望这有帮助!

转载注明原文:sql – 日期范围的MIN或MAX值 – 根据产品ID,价格和日期范围确定给定日期范围的最低价格 - 代码日志