如何在SQL中将长格式表转换为宽格式表
长格式表转宽格式表的SQL实现方案
首先明确结论:该需求完全可以通过SQL实现,且绝大多数同表行转列场景下,不需要依赖UNION语法,通用写法的执行效率会远高于UNION相关的拼接方案。
你当前使用的是典型的EAV(实体-属性-值)长表结构,核心需求是将同一id下分散在多行的属性值,透传为单行下的多个独立列,也就是常见的行转列操作。
方案1:条件聚合(全场景优先推荐)
这是跨所有SQL方言兼容性最好、执行成本最低的实现方式,仅需一次全表扫描即可完成透视,没有重复扫描的额外开销。
对应的查询写法如下,假设你的长表表名为eav_data:
SELECT id, -- 匹配key为users的行,取出对应的value作为users列 MAX(CASE WHEN `key` = 'users' THEN value END) AS users, -- 匹配key为politics的行,取出对应的value作为politics列 MAX(CASE WHEN `key` = 'politics' THEN value END) AS politics FROM eav_data -- 按id分组,把同一实体的所有属性值聚合到同一行 GROUP BY id;
逻辑说明:
- 按id做分组后,同一个id对应的所有key、value都会被纳入同一个聚合组
- 通过CASE判断筛选出匹配目标key的行,取出对应的value
- 因为同一id下每个key只会出现1次,这里搭配MAX/MIN这类聚合函数,不会出现数值被错误覆盖的问题,能准确取出对应的值
- 按照你提供的示例数据,该查询返回的结果和预期的宽表完全匹配:id=1行的users值为67、politics值为45,id=2行的users值为42、politics值为89。
方案2:UNION拼接实现
如果你最初抱着用UNION解决问题的思路探索,该场景下也可以通过UNION完成,只是性能会弱于方案1。常见写法是先针对每个key单独过滤出对应的数据,给其他目标列填充空值,再通过UNION拼接所有分支的结果,最后按id聚合合并同一行的空值与实际值:
SELECT id, MAX(users) AS users, MAX(politics) AS politics FROM ( -- 取出users属性的所有行 SELECT id, value AS users, NULL AS politics FROM eav_data WHERE `key` = 'users' UNION ALL -- 取出politics属性的所有行 SELECT id, NULL AS users, value AS politics FROM eav_data WHERE `key` = 'politics' ) AS union_result GROUP BY id;
两种方案的性能差异:
- 该UNION写法会触发至少2次原表扫描(每个key对应的过滤查询各扫一次表),数据量越大时性能和方案1的差距越明显
- 这种UNION写法最终还是需要通过聚合合并数据,执行逻辑本质上和方案1趋同,没有额外的灵活性优势,仅适合用来理解UNION的作用,实际生产环境不推荐使用。
补充说明:部分数据库支持专属的
PIVOT透视函数,本质也是对条件聚合逻辑的语法封装,在固定列的行转列场景下,不如手写CASE WHEN的写法兼容性好、调试方便。
内容的提问来源于stack exchange,提问作者Eugene
相关产品推荐
相关产品推荐

