递归 CTE 生成组内的所有组合子组

问题描述 投票:0回答:1

我正在尝试为由一列 (package_id) 链接但另一列 (package_item_id) 不匹配的行的所有可能组合生成聚合结果。

我有一个派生表包:

package_id package_item_id 数量 variant_id 零售价 销售价格 有库存
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

此结构支持零售网站的打包交易系统:

package_id 与一组可以作为包购买的元素相关。

package_item_id 分解了子元素(根据包的不同,数量也不同),用户可以从中选择variant_ids。

即包装:高级山地自行车,包装物品:车架,款式:蓝色、红色、黄色,包装物品:车轮,款式:窄、宽,包装物品:车座,款式:标准

我正在尝试生成一个聚合 package_id 的每个潜在组合的结果。将 package_id 和variants_ids 连接到查询字符串中并计算分组项目的总价格。数量列决定了为 package_item_id 提供的数量,因此是价格的乘数。此外,我想检查每个变体是否有足够的库存来组成包装 true (1) 或 false (0)

我试图仅针对第一个 package_id 实现的结果示例:

package_id 查询字符串 零售价 销售价格 库存
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

我一直在尝试在组合递归 CTE 中使用窗口函数来在组合 ID 下创建所有不同的组合,我希望它们能够获得我需要的聚合结果,但到目前为止我无法实现我的目标。

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

我不确定我是否正确地实现了这个目标,或者我想要实现的目标是否可能。

DB Fiddle 链接如下:

https://dbfiddle.uk/hEXZa674

common-table-expression window-functions recursive-query mariadb-10.5
1个回答
-1
投票

AFAICT 我们需要为每个

variant_id
生成
package_id
的所有可能组合。由于
package_item_id
的数量不同,这将涉及每个
package_item_id
的自连接。然而,对于不同数量的项目,通常需要动态数量的联接,而 SQL 本身并不支持这一点。

此代码片段可能会实现您正在寻找的结果,但每个

package_item_id
有三个
package_id

SELECT 
  p1.package_id,
  CONCAT('p_id=', p1.package_id, '&v_id=', p1.variant_id, 
         '&v_id=', p2.variant_id, '&v_id=', p3.variant_id) AS querystring,
  (p1.quantity * p1.retail_price + p2.quantity * p2.retail_price + p3.quantity * p3.retail_price) AS total_retail_price,
  (p1.quantity * p1.sell_price + p2.quantity * p2.sell_price + p3.quantity * p3.sell_price) AS total_sell_price,
  CASE 
    WHEN p1.stock_available >= p1.quantity AND p2.stock_available >= p2.quantity AND p3.stock_available >= p3.quantity 
    THEN 1 
    ELSE 0 
  END AS stock_available
FROM packages p1
JOIN packages p2 ON p1.package_id = p2.package_id AND p1.package_item_id <> p2.package_item_id
JOIN packages p3 ON p1.package_id = p3.package_id AND p1.package_item_id <> p3.package_item_id AND p2.package_item_id <> p3.package_item_id
GROUP BY p1.package_id, p1.variant_id, p2.variant_id, p3.variant_id;
© www.soinside.com 2019 - 2024. All rights reserved.