如何在SQL中将周期列转行,实现每个周期对应一行记录
高效实现宽表转窄表(行转列)的SQL方案
方案1:使用UNPIVOT(适用于SQL Server、Oracle等支持该语法的数据库)
这是数据库原生支持的行转列语法,优化器能更好地处理执行逻辑,性能远优于多次UNION ALL,尤其适合数据量较大的场景。
假设你的表名为employee_salary,执行以下SQL:
SELECT Code, Name, Period, Salary FROM employee_salary UNPIVOT ( Salary FOR Period IN ( [Period 1], [Period 2], [Period 3] ) ) AS unpvt;
执行后每个员工的各周期薪资会自动拆分为独立行,比如Worker 5会生成3条分别对应Period 1、Period 2、Period 3的记录,薪资均为5000。
方案2:使用CROSS JOIN配合值列表(适用于MySQL、PostgreSQL等无UNPIVOT的数据库)
如果你的数据库不支持UNPIVOT,这种方式比手动多次UNION ALL更简洁,执行效率也相当:
MySQL版本
SELECT es.Code, es.Name, p.period_name AS Period, CASE p.period_name WHEN 'Period 1' THEN es.`Period 1` WHEN 'Period 2' THEN es.`Period 2` WHEN 'Period 3' THEN es.`Period 3` END AS Salary FROM employee_salary es CROSS JOIN ( SELECT 'Period 1' AS period_name UNION ALL SELECT 'Period 2' UNION ALL SELECT 'Period 3' ) p;
PostgreSQL版本
SELECT es.Code, es.Name, p.period_name AS Period, CASE p.period_name WHEN 'Period 1' THEN es."Period 1" WHEN 'Period 2' THEN es."Period 2" WHEN 'Period 3' THEN es."Period 3" END AS Salary FROM employee_salary es CROSS JOIN ( UNNEST(ARRAY['Period 1', 'Period 2', 'Period 3']) AS period_name ) p;
额外优化
如果不需要保留薪资为0的记录,可在语句末尾添加WHERE Salary > 0过滤无效数据。
内容的提问来源于stack exchange,提问作者sql_journey93
相关产品推荐
相关产品推荐

