基于具有多个 dateFrom 和 DateTo 列的单独表构建时间线?

问题描述 投票:0回答:1

我有两个表,tblPrice 和 tblDiscount,每个表都有其值,其中包含 liveFrom 和 liveUntil 列(加上一些其他列,但我将这些列排除在外,以尝试使其更简单)。

我想做的是将价格和折扣列表显示为有序列表,它将显示价格如何根据价格变化或折扣变化而变化。

价格

价格ID 零售价 来自 活到
1 446413 1666.33 2022-01-31 11:36:21.490 2022-04-08 15:13:41.230
2 1338193 1666.33 2022-04-09 09:30:14.043 2023-04-05 09:37:21.767
3 2707357 1749.65 2023-04-05 09:37:21.767

tbl折扣

日志ID 折扣 来自 活到
1 192 0.3700 2022-01-31 11:27:45.060 2023-01-09 14:32:24.413
2 498 0.3200 2023-01-09 14:32:24.413 2023-04-11 15:40:06.460
3 639 0.3100 2023-04-11 15:40:06.460

预期结果

零售价 折扣 来自 活到
1 1666.33 0.37 2022年1月31日11:36 2022年8月4日15:13
2 1666.33 0.37 2022年9月4日09:30 2023年9月1日14:32
3 1666.33 0.32 2023年9月1日14:32 2023年5月4日09:37
4 1749.65 0.32 2023年5月4日09:37 2023年4月11日15:40
5 1749.65 0.317 2023年4月11日15:40

需要注意的是:

  • 价格不一定连续运行(请注意,tblPrice 上的第 1 行和第 2 行在价格上没有差异,它只是碰巧结束,然后在稍后的日期重新启动,因此这将给出一个产品没有有效价格的时期).
  • 我们关注的是价格,但折扣可以是实时的,而价格不是,因此在这种情况下,有效的实时日期仍然需要是价格而不是折扣。
  • 价格可能在没有折扣的情况下处于活动状态,因此有效折扣将为 0

我尝试根据日期范围将表相互连接,然后使用 case 语句来显示实时起始日期和实时截止日期,但它似乎不太正确,我只是无法完全理解逻辑。结果并没有如我所愿,我觉得我正朝着错误的方向掉进兔子洞。

到目前为止我的查询(正如我所说,我不确定这是正确的方法)是:

SELECT
    P.[priceID],
    P.[retailPrice],
    P.[liveFrom],
    P.[liveUntil],
    D.[logID],
    D.[discount],
    D.[liveFrom],
    D.[liveUntil],
    ------------
    CASE
        WHEN SDV.[liveFrom] <= P.[liveFrom] AND (SDV.[liveUntil] >= P.[liveFrom] OR SDV.[liveUntil] IS NULL) THEN
            SDV.[liveFrom]
        WHEN SDV.[liveFrom] >= P.[liveFrom] AND (SDV.[liveUntil] <= P.[liveUntil] OR P.[liveUntil] IS NULL) THEN
            P.[liveFrom]
        ELSE
            '1900-01-01 00:00:00'
    END AS [EFFECTIVE_FROM],
    CASE
        WHEN ISNULL(P.[liveUntil],@date) < ISNULL(SDV.[liveUntil],@date) THEN
            P.[liveUntil]
        ELSE
            SDV.[liveUntil]
    END AS [EFFECTIVE_UNTIL]
FROM 
    tblPrice P
    INNER JOIN tblDiscount D ON P.[supplierDiscountID] = D.[supplierDiscountID]
        AND ((
                D.[liveFrom] <= P.[liveFrom] 
                AND (D.[liveUntil] >= P.[liveFrom] OR D.[liveUntil] IS NULL) 
            ) OR
            (
                D.[liveFrom] >= P.[liveFrom]
                AND (D.[liveFrom] <= P.[liveUntil] OR P.[liveUntil] IS NULL)
            ))
ORDER BY
    P.[liveFrom],
    D.[liveFrom];

我没有得到我想要的结果,而且我要走的路线似乎也不正确。有人可以给我任何指示或想法吗?

sql sql-server datetime gaps-and-islands timeline
1个回答
0
投票

您必须查找在价格结束之前开始并在价格开始之后结束的折扣行,因此这可能是一种方法:

-- Sample data
declare @tblPrice table
    ( priceID int not null, retailPrice money not null
    , liveFrom datetime not null, liveUntil datetime)
declare @tblDiscount table
    ( logID int not null, discount decimal(5,4) not null
    , liveFrom datetime not null, liveUntil datetime)
insert into @tblPrice values
     ( 446413,1666.33,'2022-01-31T11:36:21.490','2022-04-08T15:13:41.230')
    ,(1338193,1666.33,'2022-04-09T09:30:14.043','2023-04-05T09:37:21.767')
    ,(2707357,1749.65,'2023-04-05T09:37:21.767', null)
insert into @tblDiscount values
     (192, 0.37, '2022-01-31T11:27:45.060','2023-01-09T14:32:24.413')
    ,(498, 0.32, '2023-01-09T14:32:24.413','2023-04-11T15:40:06.460')
    ,(639, 0.317,'2023-04-11T15:40:06.460', null)

-- Prices with discounts available or without any discount
select P.retailPrice
    , isNull(D.discount, 0) as discount
    , case when P.liveFrom  >= isNull(D.liveFrom, P.liveFrom)
        then P.liveFrom
        else D.liveFrom
      end as liveFrom
    , case when P.liveUntil <= isNull(D.liveUntil, P.liveUntil)
        then P.liveUntil
        else D.liveUntil
      end as liveUntil
from @tblPrice P
left join @tblDiscount D on
     -- P.supplierDiscountID = D.supplierDiscountID and
     D.liveFrom <= isNull(P.liveUntil, getDate())
 and P.liveFrom <= isNull(D.liveUntil, getDate())

union all
-- Prices that begins before 1st discount available
select x.retailPrice
    , 0 as discount
    , x.liveFrom
    , x.lf as liveUntil
from (
select P.retailPrice, P.liveFrom
    , min(D.liveFrom) lf
from @tblPrice P
left join @tblDiscount D on
     -- P.supplierDiscountID = D.supplierDiscountID and
     D.liveFrom <= isNull(P.liveUntil, getDate())
 and P.liveFrom <= isNull(D.liveUntil, getDate())
group by P.priceID, P.retailPrice, P.liveFrom
having min(D.liveFrom)
     > P.liveFrom
) x

union all
-- Prices that ends after last discount available
select x.retailPrice
    , 0 as discount
    , x.lu as liveFrom
    , x.liveUntil
from (
select P.retailPrice, P.liveUntil
    , max(isNull(D.liveUntil, getDate())) lu
from @tblPrice P
left join @tblDiscount D on
     -- P.supplierDiscountID = D.supplierDiscountID and
     D.liveFrom <= isNull(P.liveUntil, getDate())
 and P.liveFrom <= isNull(D.liveUntil, getDate())
group by P.priceID, P.retailPrice, P.liveUntil
having max(isNull(D.liveUntil, getDate()))
     < isNull(P.liveUntil, getDate())
) x

order by liveFrom
© www.soinside.com 2019 - 2024. All rights reserved.