I am trying produce an aggregated result for all possible combinations of rows linked by one column (package_id) but where another column (package_item_id) does not match.
I have a derived table packages:
| package_id | package_item_id | quantity | variant_id | retail_price | sell_price | stock_available |
|---|---|---|---|---|---|---|
| 1000001 | 28 | 1 | 3399 | 489.00 | 342.30 | 26 |
| 1000001 | 28 | 1 | 3400 | 489.00 | 342.30 | 23 |
| 1000001 | 28 | 1 | 3409 | 489.00 | 342.30 | 81 |
| 1000001 | 29 | 1 | 821 | 44.95 | 35.95 | 741 |
| 1000001 | 30 | 1 | 807 | 39.95 | 31.95 | 162 |
| 1000002 | 31 | 1 | 6369 | 519.00 | 363.30 | 93 |
| 1000002 | 31 | 1 | 6371 | 519.00 | 363.30 | 60 |
| 1000002 | 31 | 1 | 6372 | 519.00 | 363.30 | 40 |
| 1000002 | 31 | 1 | 19289 | 519.00 | 363.30 | 24 |
| 1000002 | 31 | 1 | 19290 | 519.00 | 363.30 | 20 |
| 1000002 | 31 | 1 | 21774 | 519.00 | 363.30 | 14 |
| 1000002 | 31 | 1 | 23045 | 519.00 | 363.30 | 0 |
| 1000002 | 37 | 1 | 821 | 44.95 | 35.95 | 741 |
| 1000002 | 38 | 1 | 807 | 39.95 | 31.95 | 162 |
This structure supports a package deal system for a retail website:
package_id relates to a group of elements that can be purchased as package.
package_item_id breaks down the sub elements (of varying number depending on the package) where the user may have a choice of variant_ids to select from.
i.e. Package: Premium Mountain Bike, Package Item: Frame, Variants: Blue,Red,Yellow, Package Item: Wheels, Variants: Narrow, Wide, Package Item: Saddle, Variants: Standard
I am trying to produce a result which aggregates every potential combination for a package_id. Concatenating the package_id & variants_ids into a querystring and calculating the total prices for grouped items. The quantity column determines the amount supplied for package_item_id so is a multiplier for the prices. Furthermore I would like to check there is sufficient stock of each variant to make up the package true (1) or false (0)
An example of the result I am trying to achieve for just the first package_id:
| package_id | querystring | retail_price | sell_price | in_stock |
|---|---|---|---|---|
| 1000001 | p_id=1000001&v_id=3399&v_id=819&v_id=807 |
598.9 | 430.2 | 1 |
| 1000001 | p_id=1000001&v_id=3400&v_id=819&v_id=807 |
598.9 | 430.2 | 1 |
| 1000001 | p_id=1000001&v_id=3409&v_id=819&v_id=807 |
598.9 | 430.2 | 1 |
| 1000001 | p_id=1000001&v_id=3399&v_id=821&v_id=807 |
573.9 | 410.2 | 1 |
| 1000001 | p_id=1000001&v_id=3400&v_id=821&v_id=807 |
573.9 | 410.2 | 1 |
| 1000001 | p_id=1000001&v_id=3409&v_id=821&v_id=807 |
573.9 | 410.2 | 1 |
I have been attempting to use window functions in combination recursive CTE to create all different combinations under combination ID which I was hoping to them group to get the aggerated result I need but so far I can't achieve what I am aiming to do.
WITH RECURSIVE cte_packages AS (
SELECT
package_id,
DENSE_RANK() OVER (ORDER BY package_id) AS p_group,
package_item_id,
DENSE_RANK() OVER (PARTITION BY package_id ORDER BY package_item_id) AS pi_group,
ROW_NUMBER() OVER (PARTITION BY package_item_id ORDER BY variant_id) AS pi_group_row,
COUNT(*) OVER (PARTITION BY package_item_id) AS pi_group_row_count,
quantity,
variant_id,
retail_price,
sell_price,
stock_available
FROM packages
),
cte_combinations AS
(
SELECT
0 AS combination_id,
p_group,
p_group AS next_p_group,
pi_group,
pi_group_row,
CASE
WHEN pi_group_row < pi_group_row_count
THEN pi_group_row + 1
ELSE 1
END
AS next_pi_group_row,
pi_group_row_count,
package_id,
package_item_id,
quantity,
variant_id,
retail_price,
sell_price,
stock_available
FROM
cte_packages
WHERE
p_group = 1 AND pi_group = 1 AND pi_group_row = 1
UNION
SELECT
cc.combination_id + 1,
cpi.p_group,
CASE
WHEN cpi.pi_group_row = cpi.pi_group_row_count
THEN cpi.p_group + 1
ELSE cpi.p_group
END
AS next_p_group,
cpi.pi_group,
cpi.pi_group_row,
CASE
WHEN cpi.pi_group_row < cpi.pi_group_row_count
THEN cpi.pi_group_row + 1
ELSE 1
END
AS next_pi_group_row,
cpi.pi_group_row_count,
cpi.package_id,
cpi.package_item_id,
cpi.quantity,
cpi.variant_id,
cpi.retail_price,
cpi.sell_price,
cpi.stock_available
FROM
cte_combinations cc
INNER JOIN
cte_packages cpi
ON cpi.p_group = cc.next_p_group AND cpi.pi_group = 1 AND cpi.pi_group_row = cc.next_pi_group_row
LEFT JOIN
cte_packages opi
ON opi.p_group = cpi.p_group AND opi.pi_group <> 1
)
SELECT * FROM cte_combinations
I am not sure if I am approaching this correctly or is what I'm trying to achieve is possible.
DB Fiddle link below:
AFAICT we need to generate all possible combinations of
variant_idfor eachpackage_id. Since the number ofpackage_item_idvaries, this will involve a self-join for eachpackage_item_id. However, with varying numbers of items, one would typically need a dynamic number of joins, which SQL does not natively support.This snippet would probably achieve the result you're looking for but only for three
package_item_idvalues perpackage_id