I want to create a list similar to the result. This should provide an overview of the net prices of a product. Always within the product's duration (start/end date).
A product can have one or more discounts. If there are multiple discounts, the percentages should be added together. So, in a period of 2x 5%, the total is 10%.
The discount may have a different start/end date than the product. The end date can be later than that of the product, but then the product's end date applies. The same goes for the start date.
The result table should fill in with an query. I'm having difficulty splitting the lines into lines per date range.
Example:
Product
product_id start_date end_date gross_price
1 01-01-2023 01-01-2024 5
2 01-01-2023 01-01-2024 10
3 01-01-2023 01-01-2024 20
product_discount
product_id product_discount_id percentage start_date end_date
1 1 10 01-01-2023 01-01-2024
2 2 10 01-01-2023 01-07-2023
3 3 5 01-01-2023 01-01-2024
3 4 5 01-01-2023 01-07-2023
Result:
product_id start_date end_date gross_price precentage net_price
1 01-01-2023 01-01-2024 5 10 4.50
2 01-01-2023 30-06-2023 10 10 9.00
2 01-07-2023 01-01-2024 10 0 10.00
3 01-01-2023 30-06-2023 20 10 18.00
3 01-07-2023 01-01-2024 20 5 19.00
I've no idea where to start..
2条答案
按热度按时间j2cgzkjk1#
The simplest and most naive case is that...
Which means that...
biswetbf2#
This can be done by:
The above logic is performed independently for each product_id value.
This solution assumes exclusive end dates, where a product offering or discount ends at
00:00:00
on the given date - effectively ending at roughly23:59:59.999999
the day before. The end of one interval exactly equals the start of the next interval. This works well with date range comparisons and avoids having to do off-by-one adjustments during the calculations.A standard comparison for overlapping intervals is
A.start < B.end AND B.start < A.end
. This nicely handles all variations of partially or fully overlapping intervals.(Normally with date-range overlaps, it is necessary to calculate the overlap portion with
GREATEST(A.start, B.start) AS overlap_start
andLEAST(A.end, B.end) AS overlap_end
. However, the way the date ranges have been defined here, they would never be further divided, so their start/end dates can be used directly.)For the final merge, the logic uses a nested query that flags each rows as being either the same (0) or different (1) from the prior row. It then calculates a running sum of these values, which becomes the key
GROUP BY
value use to merge similar rows. The combined start and end dates are then just the min(start) and max(end) of the combined rows.The code below uses several Common Table Expressions (CTEs) to build up the list of dates and date intervals before feeding those intermediate results into the final query.
Results:
Note that the transition dates between discounts for products 2 and 3 are slightly off. This seems to be a problem with the end-date interpretations as being either inclusive vs exclusive. I have assumed exclusive end-dates, which seemed consistent with the sample data having end-dates at the first of the next month, but where the discount is actually valid through the end of the last day of the previous month.
The algorithm can be changed to support inclusive end-dates by temporarily adding one day to those dates to use in the calculations. (Date range algorithms just work better this way.) The end-dates can be converted back in the end result.
However, even when I change the logic to assume inclusive end-dates, the results still do not match the OP expected results. There is no way that a discount end-date of 1 July can become an end-date of 30 June.
| product_id | start_date | end_date | gross_price | percentage | net_price |
| ------------ | ------------ | ------------ | ------------ | ------------ | ------------ |
| 1 | 2023-01-01 | 2024-01-01 | 5 | 10 | 4.50 |
| 2 | 2023-01-01 | 2023-07-01 | 10 | 10 | 9.00 |
| 2 | 2023-07-02 | 2024-01-01 | 10 | 0 | 10.00 |
| 3 | 2023-01-01 | 2023-07-01 | 20 | 10 | 18.00 |
| 3 | 2023-07-02 | 2024-01-01 | 20 | 5 | 19.00 |
See this db<>fiddle for a working demo. This includes both the above exclusive end-date solution and an alternate solution that assumes inclusive end dates. It also includes additional test data that demonstrates the final merging of adjacent ranges having the same discount.