簡體   English   中英

給定兩個日期范圍折扣表和產品價格,計算日期范圍最終價格

[英]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.

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