I have two tables, tblPrice and tblDiscount, each have their value with a liveFrom and liveUntil column (plus some other columns but I'm leaving those out to try and make this more straightforward).
What I'm trying to do is display a list of prices and discounts as an ordered list which will show how prices have changed based on the price changing or the discount changing.
tblPrice
row | priceID | retailPrice | liveFrom | LiveUntil |
---|---|---|---|---|
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 | NULL |
tblDiscount
row | logID | discount | liveFrom | LiveUntil |
---|---|---|---|---|
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 | NULL |
expected result
row | retailPrice | discount | liveFrom | liveUntil |
---|---|---|---|---|
1 | 1666.33 | 0.37 | 31/01/2022 11:36 | 08/04/2022 15:13 |
2 | 1666.33 | 0.37 | 09/04/2022 09:30 | 09/01/2023 14:32 |
3 | 1666.33 | 0.32 | 09/01/2023 14:32 | 05/04/2023 09:37 |
4 | 1749.65 | 0.32 | 05/04/2023 09:37 | 11/04/2023 15:40 |
5 | 1749.65 | 0.317 | 11/04/2023 15:40 | NULL |
Things to note are:
I've tried joining the tables on each other based on a date range and then using a case statement to show the live from and live until dates but it just doesn't seem right and I just can't quite get the logic. The results aren't coming up how I'd hope and I feel I'm going down a rabbit hole in the wrong direction.
My query so far (which as said I'm not sure is the right way to go) is:
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];
I'm not getting the results I'm wanting and the route I'm going down just doesn't seem right. Can anybody give me any pointers or ideas please?
You have to look for discount rows that begins before the price ends and ends after the price begins, so this could be a way to do it:
-- 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