PL/SQL实现Unpivot列转行:单ID仅一行问题求解
解决宽表转多行窄表的Union/Unpivot实现问题
看起来你是要把这种“宽格式”的表转换成“长格式”,但之前用Union或Unpivot时没得到预期的多行结果,大概率是用法上的小问题,我给你两种靠谱的解决方法:
方法一:用UNION ALL(别用UNION!)
你之前可能踩了UNION的坑——它会自动去重,如果同一ID的几行数据完全相同,就会被合并成一行。换成UNION ALL就能保留所有行,写法非常直接:
SELECT ID, A_1 AS A, B_1 AS B FROM your_table UNION ALL SELECT ID, A_2 AS A, B_2 AS B FROM your_table UNION ALL SELECT ID, A_3 AS A, B_3 AS B FROM your_table ORDER BY ID;
这个逻辑就是把每组A_n/B_n分别作为独立的查询结果,再把三个结果集拼接起来,UNION ALL会原封不动保留所有行,不会做去重操作。
方法二:用Cross Apply/Lateral Join(更灵活的行转列方式)
如果你的数据库支持CROSS APPLY(比如SQL Server)或者LATERAL JOIN(比如PostgreSQL、MySQL 8.0+),这种方式比传统Unpivot更直观,特别适合处理成对的列:
-- SQL Server 写法 SELECT t.ID, u.A, u.B FROM your_table t CROSS APPLY ( VALUES (A_1, B_1), (A_2, B_2), (A_3, B_3) ) u(A, B) ORDER BY t.ID; -- PostgreSQL/MySQL 8.0+ 写法 SELECT t.ID, u.A, u.B FROM your_table t LATERAL ( VALUES (A_1, B_1), (A_2, B_2), (A_3, B_3) ) u(A, B) ORDER BY t.ID;
这种方式相当于给每一行原始数据,生成三行对应A_1/B_1、A_2/B_2、A_3/B_3的记录,完美匹配你的需求。
如果你一定要用传统UNPIVOT
传统Unpivot默认只能处理单个列的拆分,所以需要分别拆A列和B列,再通过ID和序号关联起来:
WITH A_data AS ( SELECT ID, A_val AS A, -- 提取序号数字,用来关联对应的B列 CAST(REPLACE(seq_col, 'A_', '') AS INT) AS seq FROM your_table UNPIVOT ( A_val FOR seq_col IN (A_1, A_2, A_3) ) up ), B_data AS ( SELECT ID, B_val AS B, CAST(REPLACE(seq_col, 'B_', '') AS INT) AS seq FROM your_table UNPIVOT ( B_val FOR seq_col IN (B_1, B_2, B_3) ) up ) SELECT a.ID, a.A, b.B FROM A_data a JOIN B_data b ON a.ID = b.ID AND a.seq = b.seq ORDER BY a.ID;
这个方法的核心是把A和B列分别Unpivot后,通过提取的序号(1、2、3)来确保每一组A_n和B_n对应上,避免数据错位。
总结一下,你之前的问题大概率是用了UNION(去重导致合并行),或者Unpivot时没处理好成对列的关联,试试上面的方法就能解决啦。
内容的提问来源于stack exchange,提问作者Rotem87
相关产品推荐
相关产品推荐

