You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PostgreSQL优化含多COUNT CASE列的员工年度统计查询

简化部门年度任职员工数统计查询的方案

问题背景

需要统计各部门在指定年份的任职员工数量,现有表Table包含员工ID、部门名称department_name、入职日期date_start、离职日期date_end字段。当前通过重复的COUNT(CASE)语句实现按年份列展示结果,但年份增多时脚本会极度冗长,需简化查询逻辑。

当前使用的查询代码:

select department_name,
       count(
             case 
             when employee.date_start <= '2018-01-01' and employee.date_end >= '2018.12.31'
             then 1 end) as emp2018, 
       count(
             case 
             when employee.date_start <= '2019-01-01' and employee.date_end >= '2019.12.31'
             then 1 end) as emp2019, 
       count(
             case 
             when employee.date_start <= '2020-01-01' and employee.date_end >= '2020.12.31'
             then 1 end) as emp2020
from Table
group by department_name;

简化方案

1. 固定年份场景:集中定义年份,减少重复代码

通过CTE(公共表表达式)统一定义需要统计的年份,再通过关联和聚合生成结果,新增年份仅需修改CTE和SELECT中的对应行:

WITH years AS (
    SELECT 2018 AS year UNION ALL
    SELECT 2019 UNION ALL
    SELECT 2020 -- 新增年份仅需在此添加UNION ALL行
)
SELECT 
    t.department_name,
    SUM(CASE WHEN y.year = 2018 THEN 1 ELSE 0 END) AS `2018`,
    SUM(CASE WHEN y.year = 2019 THEN 1 ELSE 0 END) AS `2019`,
    SUM(CASE WHEN y.year = 2020 THEN 1 ELSE 0 END) AS `2020` -- 新增年份对应添加此行
FROM years y
CROSS JOIN (SELECT DISTINCT department_name FROM `Table`) d
LEFT JOIN `Table` t
    ON d.department_name = t.department_name
    AND t.date_start <= CONCAT(y.year, '-01-01')
    AND t.date_end >= CONCAT(y.year, '-12-31') -- 统一日期格式避免解析错误
GROUP BY t.department_name;

2. 支持PIVOT的数据库(如SQL Server、Oracle):用PIVOT转置结果

利用数据库原生的PIVOT功能,先按部门和年份统计数量,再转置为列格式:

WITH years AS (
    SELECT 2018 AS year UNION ALL
    SELECT 2019 UNION ALL
    SELECT 2020
),
emp_year_stats AS (
    SELECT 
        t.department_name,
        y.year,
        COUNT(t.employee_id) AS emp_count
    FROM years y
    LEFT JOIN `Table` t
        ON t.date_start <= CONCAT(y.year, '-01-01')
        AND t.date_end >= CONCAT(y.year, '-12-31')
    GROUP BY t.department_name, y.year
)
SELECT *
FROM emp_year_stats
PIVOT (
    SUM(emp_count)
    FOR year IN ([2018], [2019], [2020]) -- 新增年份在此添加对应项
) AS pivot_result;

3. 动态年份场景:用动态SQL自动适配年份列表

如果年份需要频繁变动,可使用动态SQL,仅需修改年份列表变量即可生成对应查询:

以MySQL为例:

-- 仅需修改此处的年份列表
SET @target_years = '2018,2019,2020';
SET @sql = '';

-- 生成年份对应的统计列语句
SELECT GROUP_CONCAT(
    CONCAT('SUM(CASE WHEN y.year = ', year, ' THEN 1 ELSE 0 END) AS `', year, '`')
) INTO @sql
FROM (
    SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(@target_years, ',', n), ',', -1) AS year
    FROM (SELECT 1 n UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5) numbers
    WHERE n <= LENGTH(@target_years) - LENGTH(REPLACE(@target_years, ',', '')) + 1
) AS year_list;

-- 拼接完整查询语句
SET @sql = CONCAT(
    'WITH years AS (',
    GROUP_CONCAT(CONCAT('SELECT ', year, ' AS year') SEPARATOR ' UNION ALL '),
    ') SELECT t.department_name, ', @sql, 
    ' FROM years y CROSS JOIN (SELECT DISTINCT department_name FROM `Table`) d ',
    ' LEFT JOIN `Table` t ON d.department_name = t.department_name ',
    ' AND t.date_start <= CONCAT(y.year, ''-01-01'') ',
    ' AND t.date_end >= CONCAT(y.year, ''-12-31'') ',
    ' GROUP BY t.department_name;'
);

-- 执行动态SQL
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

注意事项

  • 统一日期格式:原查询中混用'2018-01-01'和'2018.12.31',需改为一致的'YYYY-MM-DD'格式,避免数据库解析错误。
  • 左连接确保无数据的部门也能显示:通过CROSS JOIN生成所有部门和年份的组合,再LEFT JOIN原表,保证即使部门在某年份无任职员工,也会显示0值。

内容的提问来源于stack exchange,提问作者Kiki

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 23:33:22