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

如何基于EXTRACT(YEAR_MONTH)动态生成列实现行转列(无需CASE)

动态行列转置需求:根据日期区间自动生成年月列

现有查询与结果

当前使用的SQL语句:

SELECT 
  products.sku, 
  SUM(sells_products.quantity) AS quantity, 
  EXTRACT(YEAR_MONTH FROM sells.date) AS period 
FROM sells, sells_products, products 
WHERE sells.id = sells_products.id_sell 
  AND (products.sku = '1111' OR products.sku = '2222') 
  AND sells_products.id_product = products.id 
  AND sells.date BETWEEN '2023-05-01' AND '2023-07-12' 
GROUP BY products.sku, EXTRACT(YEAR_MONTH FROM sells.date)

查询返回的行式结果:

SKU | QNT| PERIOD
1111 | 13 | 202305
1111 | 44 | 202306
1111 | 24 | 202307
2222 | 64 | 202305
2222 | 12 | 202306
2222 | 56 | 202307

期望输出

需要将每个period(年月)转为单独的列,且不想手动写CASE语句,希望根据指定的日期区间自动生成对应列:

SKU | 202305 | 202306 | 202307
1111 |   13   |   44   |   24
2222 |   64   |   12   |   56

解决方案

纯静态SQL无法实现自动生成动态列,必须借助动态SQL(根据日期区间自动构造查询语句)或数据库自带的透视功能。以下是主流数据库的实现方式:

MySQL 实现(存储过程动态生成透视语句)

DELIMITER //
CREATE PROCEDURE dynamic_monthly_pivot()
BEGIN
  -- 自动提取日期区间内的所有年月,生成列定义
  SET @cols = NULL;
  SELECT GROUP_CONCAT(DISTINCT CONCAT('SUM(CASE WHEN period = ', period, ' THEN quantity ELSE 0 END) AS `', period, '`'))
  INTO @cols
  FROM (
    SELECT DISTINCT EXTRACT(YEAR_MONTH FROM sells.date) AS period
    FROM sells
    WHERE sells.date BETWEEN '2023-05-01' AND '2023-07-12'
  ) AS unique_periods;

  -- 构造完整的透视查询
  SET @sql = CONCAT(
    'SELECT sku, ', @cols, ' FROM (
        SELECT 
          products.sku, 
          SUM(sells_products.quantity) AS quantity, 
          EXTRACT(YEAR_MONTH FROM sells.date) AS period 
        FROM sells
        JOIN sells_products ON sells.id = sells_products.id_sell
        JOIN products ON sells_products.id_product = products.id
        WHERE (products.sku = ''1111'' OR products.sku = ''2222'') 
          AND sells.date BETWEEN ''2023-05-01'' AND ''2023-07-12'' 
        GROUP BY products.sku, period
      ) AS src_data
      GROUP BY sku'
  );

  -- 执行动态生成的SQL
  PREPARE stmt FROM @sql;
  EXECUTE stmt;
  DEALLOCATE PREPARE stmt;
END //
DELIMITER ;

-- 调用存储过程获取结果
CALL dynamic_monthly_pivot();

注:这里的CASE语句是动态生成的,无需手动编写每个年月的判断逻辑。

PostgreSQL 实现(crosstab函数+动态SQL)

首先确保安装tablefunc扩展(若未安装):

CREATE EXTENSION IF NOT EXISTS tablefunc;

然后执行动态透视脚本:

DO $$
DECLARE
  column_list text;
BEGIN
  -- 提取日期区间内的所有年月,生成列名列表
  SELECT string_agg(DISTINCT quote_ident(period::text), ', ')
  INTO column_list
  FROM (
    SELECT DISTINCT EXTRACT(YEAR_MONTH FROM sells.date) AS period
    FROM sells
    WHERE sells.date BETWEEN '2023-05-01' AND '2023-07-12'
  ) AS unique_periods;

  -- 动态执行crosstab透视查询
  EXECUTE format(
    'SELECT * FROM crosstab(
      ''SELECT sku, period::text, quantity FROM (
          SELECT 
            products.sku, 
            EXTRACT(YEAR_MONTH FROM sells.date) AS period, 
            SUM(sells_products.quantity) AS quantity 
          FROM sells
          JOIN sells_products ON sells.id = sells_products.id_sell
          JOIN products ON sells_products.id_product = products.id
          WHERE (products.sku = ''''1111'''' OR products.sku = ''''2222'''') 
            AND sells.date BETWEEN ''''2023-05-01'''' AND ''''2023-07-12'''' 
          GROUP BY products.sku, period
        ) AS src_data
        ORDER BY sku, period'',
      ''SELECT DISTINCT EXTRACT(YEAR_MONTH FROM sells.date)::text FROM sells WHERE sells.date BETWEEN ''''2023-05-01'''' AND ''''2023-07-12'''' ORDER BY 1''
    ) AS pivot_result(sku text, %s);', column_list
  );
END $$;

SQL Server 实现(动态PIVOT)

DECLARE @column_list NVARCHAR(MAX);
DECLARE @pivot_sql NVARCHAR(MAX);

-- 提取日期区间内的所有年月,生成列名(带引号)
SELECT @column_list = STRING_AGG(DISTINCT QUOTENAME(FORMAT(sells.date, 'yyyyMM')), ', ')
FROM sells
WHERE sells.date BETWEEN '2023-05-01' AND '2023-07-12';

-- 构造动态PIVOT查询语句
SET @pivot_sql = N'
SELECT sku, ' + @column_list + '
FROM (
    SELECT 
      products.sku, 
      SUM(sells_products.quantity) AS quantity, 
      FORMAT(sells.date, ''yyyyMM'') AS period 
    FROM sells
    JOIN sells_products ON sells.id = sells_products.id_sell
    JOIN products ON sells_products.id_product = products.id
    WHERE (products.sku = ''1111'' OR products.sku = ''2222'') 
      AND sells.date BETWEEN ''2023-05-01'' AND ''2023-07-12'' 
    GROUP BY products.sku, FORMAT(sells.date, ''yyyyMM'')
) AS src_data
PIVOT (
  SUM(quantity)
  FOR period IN (' + @column_list + ')
) AS pivot_result;';

-- 执行动态SQL
EXEC sp_executesql @pivot_sql;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 18:44:51