Oracle查询:将多组关联版本列合并为单列并保留主键
实现Oracle列转行需求的解决方案
当然可以搞定这个需求!这本质上是典型的**列转行(Unpivot)**场景,针对Oracle数据库,我给你两种实用的实现方案,都能完美匹配你的输出要求:
方案一:使用UNPIVOT语法(推荐,简洁直观)
UNPIVOT是Oracle专门用于列转行的语法,适合这种有规律的列名转换场景:
SELECT pk, aaa, bbb, ccc FROM ( -- 先把原表的AA/BB/CC系列列重命名为带版本号的格式 SELECT pk, aa AS aaa_1, aa2 AS aaa_2, aa3 AS aaa_3, aa4 AS aaa_4, aa5 AS aaa_5, bb AS bbb_1, bb2 AS bbb_2, bb3 AS bbb_3, bb4 AS bbb_4, bb5 AS bbb_5, cc AS ccc_1, cc2 AS ccc_2, cc3 AS ccc_3, cc4 AS ccc_4, cc5 AS ccc_5 FROM your_table_name -- 替换成你的实际表名 ) -- 分别对AA、BB、CC系列执行列转行 UNPIVOT ( aaa FOR version_aaa IN (aaa_1 AS 1, aaa_2 AS 2, aaa_3 AS 3, aaa_4 AS 4, aaa_5 AS 5) ) UNPIVOT ( bbb FOR version_bbb IN (bbb_1 AS 1, bbb_2 AS 2, bbb_3 AS 3, bbb_4 AS 4, bbb_5 AS 5) ) UNPIVOT ( ccc FOR version_ccc IN (ccc_1 AS 1, ccc_2 AS 2, ccc_3 AS 3, ccc_4 AS 4, ccc_5 AS 5) ) -- 确保同一版本的AA/BB/CC对应,同时过滤空版本(题目说AA存在则BB/CC必存在) WHERE version_aaa = version_bbb AND version_bbb = version_ccc AND aaa IS NOT NULL ORDER BY pk, version_aaa;
方案说明:
- 首先将原表的
AA/AA2等列重命名为带版本号的aaa_1/aaa_2,方便后续关联; - 三次
UNPIVOT分别把AA、BB、CC系列的列转成行,同时保留版本号; - 通过版本号关联,确保同一版本的三个值对应成一行;
- 过滤掉
aaa为空的行(因为题目明确“若存在AA则必有BB和CC”,所以只要AA非空,BB/CC肯定也非空)。
方案二:使用CONNECT BY+CASE表达式(兼容低版本Oracle)
如果你的Oracle版本不支持UNPIVOT(比如11g之前),可以用递归生成行结合CASE表达式实现:
SELECT pk, -- 根据版本号取对应列的值 CASE level WHEN 1 THEN aa WHEN 2 THEN aa2 WHEN 3 THEN aa3 WHEN 4 THEN aa4 WHEN 5 THEN aa5 END AS aaa, CASE level WHEN 1 THEN bb WHEN 2 THEN bb2 WHEN 3 THEN bb3 WHEN 4 THEN bb4 WHEN 5 THEN bb5 END AS bbb, CASE level WHEN 1 THEN cc WHEN 2 THEN cc2 WHEN 3 THEN cc3 WHEN 4 THEN cc4 WHEN 5 THEN cc5 END AS ccc FROM your_table_name -- 替换成你的实际表名 -- 生成1-5的版本行,每个PK对应5行 CONNECT BY level <= 5 AND PRIOR pk = pk AND PRIOR SYS_GUID() IS NOT NULL -- 防止递归循环 -- 过滤空版本 WHERE CASE level WHEN 1 THEN aa WHEN 2 THEN aa2 WHEN 3 THEN aa3 WHEN 4 THEN aa4 WHEN 5 THEN aa5 END IS NOT NULL ORDER BY pk, level;
方案说明:
- 用
CONNECT BY level <=5为每个PK生成5行(对应AA到AA5的5个版本); - 通过
CASE表达式根据level值取对应版本的AA/BB/CC列; - 过滤掉AA为空的行,只保留有数据的版本;
PRIOR SYS_GUID() IS NOT NULL是为了避免递归时出现循环问题。
这两种方案都能完美输出你想要的结果,比如针对你提供的示例数据,PK=3会生成3行,完全符合要求。
内容的提问来源于stack exchange,提问作者junpet




