You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

技术问询:MySQL结果一行转多行方法及订单日程自动化需求

嘿,针对你的两个问题,我来一步步帮你解决:

问题1:如何遍历MySQL查询结果,将单条数据拆分为多行?

要把单条数据按指定次数拆分成多行,核心思路是生成一个与拆分次数匹配的数字序列,再将这个序列和原表关联,从而把单条记录扩展成多行。下面分两种MySQL版本给出方案:

方案1:MySQL 8.0+(支持递归CTE)

递归CTE是最简洁的方式,先生成从1到最大拆分次数的数字序列,再关联原表过滤出需要的行数:

WITH RECURSIVE nums AS (
    SELECT 1 AS n
    UNION ALL
    SELECT n + 1 FROM nums WHERE n < (SELECT MAX(total_baths) FROM orders)
)
SELECT o.id, n AS bath_sequence
FROM orders o
JOIN nums n ON n.n <= o.total_baths;

解释:

  • nums递归CTE生成从1开始的连续数字,直到达到订单表中最大的洗浴次数(你可以替换成自己需要的字段)。
  • 关联订单表后,只要数字小于等于该订单的拆分次数,就会生成一行,这样每条订单就会被拆分成total_baths行。

方案2:MySQL 5.x(不支持CTE)

如果你的MySQL版本不支持CTE,可以用笛卡尔积生成数字序列:

SELECT o.id, (a.n + b.n * 10 + c.n * 100) + 1 AS bath_sequence
FROM orders o
CROSS JOIN (SELECT 0 AS n UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) a
CROSS JOIN (SELECT 0 AS n UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) b
CROSS JOIN (SELECT 0 AS n UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) c
WHERE (a.n + b.n * 10 + c.n * 100) + 1 <= o.total_baths;

解释:

  • 三个包含0-9的子表笛卡尔积,生成0-999的数字,加1后变成1-1000,足够覆盖大部分场景。
  • 通过WHERE条件过滤出不超过订单拆分次数的数字,实现单行转多行。

问题2:自动化日程安排中的订单洗浴任务拆分

先明确你的核心需求:

  • 从订单中提取ID、交付日期,计算洗浴次数(订单价格÷125)
  • 每个订单的洗浴任务必须在交付日期完成,每日最多完成2次/订单
  • 同一工作日可安排多个2次任务,但不能来自同一订单(这里我理解为:同一工作日内,同一个订单的任务组不能连续安排,避免集中处理)

下面是完整的SQL解决方案:

WITH RECURSIVE order_baths AS (
    -- 第一步:计算每个订单的基础信息和总洗浴次数
    SELECT 
        order_id,
        internal_delivery_date,
        order_price DIV 125 AS total_baths, -- 用整数除法计算洗浴次数,可根据需求换成ROUND/FLOOR
        -- 判断交付日期是否为工作日(0=周一,4=周五)
        CASE WHEN WEEKDAY(internal_delivery_date) BETWEEN 0 AND 4 THEN internal_delivery_date ELSE NULL END AS work_day
    FROM orders
    WHERE order_price >= 125 -- 过滤掉不需要洗浴的订单
),
task_groups AS (
    -- 递归生成任务组:每组最多2次,直到剩余次数为0
    SELECT 
        order_id,
        internal_delivery_date,
        work_day,
        LEAST(2, total_baths) AS baths_in_group,
        total_baths - LEAST(2, total_baths) AS remaining_baths,
        1 AS group_number
    FROM order_baths
    WHERE total_baths > 0
    UNION ALL
    SELECT 
        t.order_id,
        t.internal_delivery_date,
        t.work_day,
        LEAST(2, t.remaining_baths) AS baths_in_group,
        t.remaining_baths - LEAST(2, t.remaining_baths) AS remaining_baths,
        t.group_number + 1 AS group_number
    FROM task_groups t
    WHERE t.remaining_baths > 0
),
ranked_tasks AS (
    -- 给任务排名,实现同一订单的任务组在工作日内分散安排
    SELECT 
        *,
        ROW_NUMBER() OVER (PARTITION BY work_day ORDER BY order_id, group_number) AS task_rank,
        ROW_NUMBER() OVER (PARTITION BY work_day, order_id ORDER BY group_number) AS order_group_rank
    FROM task_groups
    WHERE work_day IS NOT NULL -- 只保留工作日的任务
)
-- 最终输出分散后的任务列表
SELECT 
    order_id,
    internal_delivery_date,
    work_day,
    baths_in_group,
    group_number
FROM ranked_tasks
ORDER BY work_day, order_group_rank, task_rank;

关键步骤解释:

  1. order_baths CTE:计算每个订单的总洗浴次数,同时筛选出工作日的交付日期(如果交付日期是周末,这里会排除,你可以根据需求调整)。
  2. task_groups CTE:递归拆分任务,每次取最多2次作为一个任务组,直到该订单的所有洗浴次数都被拆分完成。
  3. ranked_tasks CTE:通过窗口函数给任务排名,确保同一订单的任务组在工作日内不会连续出现,满足“同一工作日可有多组2次任务,但不可来自同一订单”的需求。
  4. 最后按工作日、订单组排名、任务排名排序,输出最终的任务安排列表。

内容的提问来源于stack exchange,提问作者F. LK

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:21:40