[英]Given two date ranged discount tables and product price, calculate date ranged final price
我有兩張季節性折扣的桌子。 在這兩個表中的每一個中都是非重疊的日期范圍,產品ID和在該日期范圍內應用的折扣。 但是,一個表的日期范圍可能與另一個表中的日期范圍重疊。 給定具有產品ID和默認價格的第三個表格,目標是在應用兩個表格的折扣后有效地計算產品ID的季節性日期范圍價格。
折扣僅在重疊期間成倍增加,例如,如果第一次折扣是從2019-07-01到2019-07-30的0.9(10%),第二次折扣是從2019-07-16到2019-08-15的0.8。 ,這意味着:從2019-07-01到2019-07-15的折扣為0.9,從2019-07-16到2019-07-30的折扣為0.72,從2019-07-31到2019-08-15的折扣為0.8 。
我設法找到了一個解決方案,首先生成一個表,在兩個折扣表中保存有序的所有開始和結束日期,然后生成所有最小不相交間隔的結果表,然后為每個間隔生成所有價格,默認,價格只有第一張表格的折扣(如果有的話),只有第二張表格的折扣價格(如果有的話),價格和兩個折扣(如果可能的話),然后取這四個價格的最小值。 請參閱下面的示例代碼。
declare @pricesDefault table (product_id int, price decimal)
insert into @pricesDefault
values
(1, 100),
(2, 120),
(3, 200),
(4, 50)
declare @discountTypeA table (product_id int, modifier decimal(4,2), startdate datetime, enddate datetime)
insert into @discountTypeA
values
(1, 0.75, '2019-06-06', '2019-07-06'),
(1, 0.95, '2019-08-06', '2019-08-20'),
(1, 0.92, '2019-05-06', '2019-06-05'),
(2, 0.75, '2019-06-08', '2019-07-19'),
(2, 0.95, '2019-07-20', '2019-09-20'),
(3, 0.92, '2019-05-06', '2019-06-05')
declare @discountTypeB table (product_id int, modifier decimal(4,2), startdate datetime, enddate datetime)
insert into @discountTypeB
values
(1, 0.85, '2019-06-20', '2019-07-03'),
(1, 0.65, '2019-08-10', '2019-08-29'),
(1, 0.65, '2019-09-10', '2019-09-27'),
(3, 0.75, '2019-05-08', '2019-05-19'),
(2, 0.95, '2019-05-20', '2019-05-21'),
(3, 0.92, '2019-09-06', '2019-09-09')
declare @pricingPeriod table(product_id int, discountedPrice decimal, startdate datetime, enddate datetime);
with allDates(product_id, dt) as
(select distinct product_id, dta.startdate from @discountTypeA dta
union all
select distinct product_id, dta.enddate from @discountTypeA dta
union all
select distinct product_id, dtb.startdate from @discountTypeB dtb
union all
select distinct product_id, dtb.enddate from @discountTypeB dtb
),
allproductDatesWithId as
(select product_id, dt, row_number() over (partition by product_id order by dt asc) 'Id'
from allDates),
sched as
(select pd.product_id, apw1.dt startdate, apw2.dt enddate
from @pricesDefault pd
join allproductDatesWithId apw1 on apw1.product_id = pd.product_id
join allproductDatesWithId apw2 on apw2.product_id = pd.product_id and apw2.Id= apw1.Id+1
),
discountAppliedTypeA as(
select sc.product_id, sc.startdate, sc.enddate,
min(case when sc.startdate >= dta.startdate and dta.enddate >= sc.enddate then pd.price * dta.modifier else pd.price end ) 'price'
from sched sc
join @pricesDefault pd on pd.product_id = sc.product_id
left join @discountTypeA dta on sc.product_id = dta.product_id
group by sc.product_id, sc.startdate , sc.enddate ),
discountAppliedTypeB as(
select daat.product_id, daat.startdate, daat.enddate,
min(case when daat.startdate >= dta.startdate and dta.enddate >= daat.enddate then daat.price * dta.modifier else daat.price end ) 'price'
from discountAppliedTypeA daat
left join @discountTypeB dta on daat.product_id = dta.product_id
group by daat.product_id, daat.startdate , daat.enddate )
select * from discountAppliedTypeB
order by product_id, startdate
計算所有可能價格的最小值是不必要的開銷。 我想生成一個最終價格並將其作為最終價格。
這是結果集:
product_id start_date end_date final_price
1 2019-05-06 00:00:00.000 2019-06-05 00:00:00.000 92.0000
1 2019-06-05 00:00:00.000 2019-06-06 00:00:00.000 100.0000
1 2019-06-06 00:00:00.000 2019-06-20 00:00:00.000 75.0000
1 2019-06-20 00:00:00.000 2019-07-03 00:00:00.000 63.7500
1 2019-07-03 00:00:00.000 2019-07-06 00:00:00.000 75.0000
1 2019-07-06 00:00:00.000 2019-08-06 00:00:00.000 100.0000
1 2019-08-06 00:00:00.000 2019-08-10 00:00:00.000 95.0000
1 2019-08-10 00:00:00.000 2019-08-20 00:00:00.000 61.7500
1 2019-08-20 00:00:00.000 2019-08-29 00:00:00.000 65.0000
1 2019-08-29 00:00:00.000 2019-09-10 00:00:00.000 100.0000
1 2019-09-10 00:00:00.000 2019-09-27 00:00:00.000 65.0000
2 2019-05-20 00:00:00.000 2019-05-21 00:00:00.000 114.0000
2 2019-05-21 00:00:00.000 2019-06-08 00:00:00.000 120.0000
2 2019-06-08 00:00:00.000 2019-07-19 00:00:00.000 90.0000
2 2019-07-19 00:00:00.000 2019-07-20 00:00:00.000 120.0000
2 2019-07-20 00:00:00.000 2019-09-20 00:00:00.000 114.0000
3 2019-05-06 00:00:00.000 2019-05-08 00:00:00.000 184.0000
3 2019-05-08 00:00:00.000 2019-05-19 00:00:00.000 138.0000
3 2019-05-19 00:00:00.000 2019-06-05 00:00:00.000 184.0000
3 2019-06-05 00:00:00.000 2019-09-06 00:00:00.000 200.0000
3 2019-09-06 00:00:00.000 2019-09-09 00:00:00.000 184.0000
我沒有看到這個解決方案更有效嗎?
我在實際產品價格表中有大約20K行的大數據集,在兩個折扣表中都有100K到200K行。
實際表的索引結構如下:產品ID是產品價格表中的聚集索引,而折扣表具有Id代理列作為聚簇索引(以及主鍵),以及(product_id,start_date,end_date)作為非聚簇指數。
您可以使用union
生成日期。 然后引入該日期有效的所有折扣,並計算總額。
這看起來像:
with prices as (
select a.product_id, v.dte
from @discountTypeA a cross apply
(values (a.startdate), (a.enddate)) v(dte)
union -- on purpose to remove duplicates
select b.product_id, v.dte
from @discountTypeB b cross apply
(values (b.startdate), (b.enddate)) v(dte)
),
p as (
select p.*, 1-a.modifier as a_discount, 1-b.modifier as b_discount, pd.price
from prices p left join
@pricesDefault pd
on pd.product_id = p.product_id left join
@discountTypeA a
on p.product_id = a.product_id and
p.dte >= a.startdate and p.dte < a.enddate left join
@discountTypeb b
on p.product_id = b.product_id and
p.dte >= b.startdate and p.dte < b.enddate
)
select p.product_id, price * (1 - coalesce(a_discount, 0)) * (1 - coalesce(b_discount, 0)) as price, a_discount, b_discount,
dte as startdate, lead(dte) over (partition by product_id order by dte) as enddate
from p
order by product_id, dte;
這是一個db <>小提琴。
這是一個計算每個日期的價格的版本。 然后,您可以直接使用它,或者使用SO上的眾多解決方案之一來計算日期范圍。
在這個例子中,我對日期限制進行了硬編碼,但如果您願意,可以從表中輕松讀取它們。
我沒有對此進行過任何性能測試,但請試一試。 如果你有正確的索引可能會更快,它會更簡單。
;with dates as (
select convert(datetime,'2019-05-06') as d
union all
select d+1 from dates where d<'2019-09-27'
)
select pricesDefault.product_id, d, pricesDefault.price as baseprice,
discountA.modifier as dA,
discountB.modifier as dB,
pricesDefault.price*isnull(discountA.modifier,1)*isnull(discountB.modifier,1) as finalprice
from @pricesDefault pricesDefault
cross join dates
left join @discountTypeA discountA on discountA.product_id=pricesDefault.product_id and d between discountA.startdate and discountA.enddate
left join @discountTypeB discountB on discountB.product_id=pricesDefault.product_id and d between discountB.startdate and discountB.enddate
order by pricesDefault.product_id, d
Option (MaxRecursion 1000)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.