簡體   English   中英

日期范圍的MIN或MAX值 - 根據產品ID,價格和日期范圍確定給定日期范圍的最低價格

[英]MIN or MAX Value for Date Ranges - Determining lowest price for a given date range based on Product ID, Price, and Date Ranges

我真的希望你們中的一些人喜歡挑戰。 我有一張產品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

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

  1. 日期與“觸摸”(即Id#3)合並為一個期間的ID和價格相同。

  2. 重疊的日期(即Id#4)合並為一個期間。

  3. 顯示每種產品的最低價格以及在什么范圍內。

  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

如果您沒有日期表,則可以輕松創建日期表。 網上有很多這方面的例子。

我希望這有幫助!

您可以將句點的開頭定義為不重疊的句點的開頭 這很棘手,但可以使用exists或除當前行之外的結束日期的累積最大值來完成。

然后,每個非重疊是組的開始。 該組可用於聚合:

select id, min(startDate) as startDate, max(endDate) as endDate, min(price) as price
from (select t.*,
             sum(case when prev_endDate < dateadd(second, -1, startDate)
                      then 1 else 0
                 end) over (partition by id order by startdate) as grp
      from (select t.*,
                   max(endDate) over (partition by id
                                      order by startdate
                                      rows between unbounded preceding and 1 preceding
                                     ) as prev_endDate
            from t
           ) t
     ) t
group by id, grp;

我不是百分百肯定這是有效的。 我只想到使用累計最大結束日期。 我很確定它涵蓋了所有重疊的情況,但我可能錯過了一些東西。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM