MySQL中如何使用递归或循环为查询动态添加自定义列
MySQL动态行转列实现方案
该需求可直接实现,无需使用递归或循环逻辑,具体实现分两种场景:
前置逻辑:给用户事件按顺序打标
首先要给每个user_id对应的记录按id升序生成序列编号,和event_1、event_2...的列名对应,MySQL 8.0及以上可用窗口函数实现:
SELECT user_id, create_date, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY id ASC) AS event_seq FROM event_table
方案1:已知最大事件次数的静态写法
如果提前确认所有用户的事件最大次数,直接写固定聚合逻辑即可,你的测试数据中用户最多有3条记录,对应写法如下:
SELECT user_id, MAX(CASE WHEN event_seq = 1 THEN create_date END) AS event_1, MAX(CASE WHEN event_seq = 2 THEN create_date END) AS event_2, MAX(CASE WHEN event_seq = 3 THEN create_date END) AS event_3 FROM ( SELECT user_id, create_date, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY id ASC) AS event_seq FROM event_table ) t GROUP BY user_id ORDER BY user_id;
输出结果完全符合预期:无对应记录的列值为NULL,按user_id维度聚合展示所有事件时间。
方案2:动态适配任意事件次数的通用写法
如果不确定用户最大事件次数,可使用MySQL预处理语句动态拼接SQL:
-- 可选:调整GROUP_CONCAT长度限制,避免事件过多时SQL片段被截断 SET SESSION group_concat_max_len = 102400; -- 1. 生成动态列的SQL片段 SET @sql = NULL; SELECT GROUP_CONCAT( DISTINCT CONCAT( 'MAX(CASE WHEN event_seq = ', event_seq, ' THEN create_date END) AS event_', event_seq ) ORDER BY event_seq ASC ) INTO @sql FROM ( SELECT ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY id ASC) AS event_seq FROM event_table ) t; -- 2. 拼接完整查询语句 SET @full_sql = CONCAT( 'SELECT user_id, ', @sql, ' FROM (SELECT user_id, create_date, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY id ASC) AS event_seq FROM event_table) t GROUP BY user_id ORDER BY user_id' ); -- 3. 预处理并执行SQL PREPARE stmt FROM @full_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
MySQL 5.x兼容处理
如果是不支持窗口函数的MySQL 5.x版本,可用用户变量替代窗口函数实现序列打标,替换上述子查询逻辑即可:
SELECT user_id, create_date, @seq := IF(@current_user = user_id, @seq + 1, 1) AS event_seq, @current_user := user_id FROM event_table CROSS JOIN (SELECT @seq := 0, @current_user := NULL) AS vars ORDER BY user_id, id ASC
内容的提问来源于stack exchange,提问作者Dylan Brugman
相关产品推荐
相关产品推荐

