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

如何在SQL中实现基于id3唯一值的多列数据表扁平化?

行转列(Pivot)实现数据表扁平化方案

这是一个典型的**行转列(Pivot)**需求,而且要适配任意数量的value列,下面分两种场景给出具体实现方案:


一、静态场景(已知id3的所有可能取值)

如果已经明确id3只会取固定值(比如1、2、3),用CASE WHEN的写法是跨数据库兼容的,也是最直观的实现方式:

SELECT
  id1,
  id2,
  -- 为每个value列按id3拆分生成新列
  MAX(CASE WHEN id3 = 1 THEN value1 END) AS value1_id3=1,
  MAX(CASE WHEN id3 = 1 THEN value2 END) AS value2_id3=1,
  MAX(CASE WHEN id3 = 2 THEN value1 END) AS value1_id3=2,
  MAX(CASE WHEN id3 = 2 THEN value2 END) AS value2_id3=2,
  MAX(CASE WHEN id3 = 3 THEN value1 END) AS value1_id3=3,
  MAX(CASE WHEN id3 = 3 THEN value2 END) AS value2_id3=3
FROM source_table
GROUP BY id1, id2;

为什么用MAX?

因为id1-id2-id3是唯一标识,每个组合只有一条数据,用MAX/MIN/ANY_VALUE(MySQL专属)这类聚合函数,只是为了满足GROUP BY后的语法要求,不会改变实际取值。

如果是SQL Server/Oracle这类支持专用PIVOT语法的数据库,也可以用如下写法,但一次只能处理一个value列,多列需要嵌套PIVOT,不如CASE WHEN直观:

-- SQL Server示例
SELECT
  id1,
  id2,
  value1_1 AS value1_id3=1,
  value2_1 AS value2_id3=1,
  value1_2 AS value1_id3=2,
  value2_2 AS value2_id3=2,
  value1_3 AS value1_id3=3,
  value2_3 AS value2_id3=3
FROM (
  SELECT
    id1,
    id2,
    CONCAT('value1_', id3) AS value1_col,
    value1,
    CONCAT('value2_', id3) AS value2_col,
    value2
  FROM source_table
) AS src
PIVOT (
  MAX(value1) FOR value1_col IN ([value1_1], [value1_2], [value1_3])
) AS p1
PIVOT (
  MAX(value2) FOR value2_col IN ([value2_1], [value2_2], [value2_3])
) AS p2;

二、动态场景(支持任意id3取值和任意数量value列)

如果id3的取值不确定,或者后续可能新增value列,就需要用动态SQL自动生成转换逻辑,完全适配任意场景:

1. MySQL/MariaDB实现

-- 1. 获取所有唯一的id3值
SET @id3_list = (SELECT GROUP_CONCAT(DISTINCT id3) FROM source_table);
-- 2. 获取所有value列(假设列名以value开头,可根据实际调整规则)
SET @value_cols = (SELECT GROUP_CONCAT(DISTINCT COLUMN_NAME) 
                   FROM INFORMATION_SCHEMA.COLUMNS 
                   WHERE TABLE_SCHEMA = DATABASE() 
                     AND TABLE_NAME = 'source_table' 
                     AND COLUMN_NAME LIKE 'value%');

-- 3. 拼接完整的SQL语句
SET @sql = CONCAT('SELECT id1, id2, ',
                  (SELECT GROUP_CONCAT(
                     CONCAT('MAX(CASE WHEN id3 = ', id3, ' THEN ', col, ' END) AS ', col, '_id3=', id3)
                   ) FROM (SELECT DISTINCT id3 FROM source_table) AS t1,
                        (SELECT COLUMN_NAME AS col FROM INFORMATION_SCHEMA.COLUMNS 
                         WHERE TABLE_SCHEMA = DATABASE() 
                           AND TABLE_NAME = 'source_table' 
                           AND COLUMN_NAME LIKE 'value%') AS t2),
                  ' FROM source_table GROUP BY id1, id2;');

-- 4. 执行动态生成的SQL
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

2. PostgreSQL实现

用PL/pgSQL编写自定义函数:

CREATE OR REPLACE FUNCTION pivot_source_table() 
RETURNS SETOF RECORD AS $$
DECLARE
  id3_values INT[];
  value_cols TEXT[];
  sql TEXT;
BEGIN
  -- 获取所有唯一id3值
  SELECT ARRAY_AGG(DISTINCT id3) INTO id3_values FROM source_table;
  -- 获取所有value列
  SELECT ARRAY_AGG(column_name) INTO value_cols 
  FROM information_schema.columns
  WHERE table_schema = current_schema() 
    AND table_name = 'source_table' 
    AND column_name LIKE 'value%';

  -- 拼接SELECT子句
  sql := 'SELECT id1, id2, ';
  FOR col IN SELECT unnest(value_cols) LOOP
    FOR id3_val IN SELECT unnest(id3_values) LOOP
      sql := sql || format('MAX(CASE WHEN id3 = %L THEN %I END) AS %I, ', 
                           id3_val, col, col || '_id3=' || id3_val);
    END LOOP;
  END LOOP;
  -- 移除末尾多余的逗号
  sql := rtrim(sql, ', ');
  sql := sql || ' FROM source_table GROUP BY id1, id2;';

  RETURN QUERY EXECUTE sql;
END;
$$ LANGUAGE plpgsql;

-- 调用函数获取结果
SELECT * FROM pivot_source_table() 
AS (id1 INT, id2 INT, value1_id3=1 TEXT, value2_id3=1 TEXT, value1_id3=2 TEXT, value2_id3=2 TEXT);

3. SQL Server实现

DECLARE @id3_list NVARCHAR(MAX), @value_cols NVARCHAR(MAX), @sql NVARCHAR(MAX);

-- 获取所有唯一id3值
SELECT @id3_list = STRING_AGG(DISTINCT CAST(id3 AS NVARCHAR), ', ') FROM source_table;
-- 获取所有value列
SELECT @value_cols = STRING_AGG(DISTINCT COLUMN_NAME, ', ') 
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'dbo' 
  AND TABLE_NAME = 'source_table' 
  AND COLUMN_NAME LIKE 'value%';

-- 拼接完整SQL
SET @sql = 'SELECT id1, id2, ' +
           (SELECT STRING_AGG(
              CONCAT('MAX(CASE WHEN id3 = ', id3, ' THEN ', col, ' END) AS ', QUOTENAME(col + '_id3=' + CAST(id3 AS NVARCHAR))),
              ', '
            ) FROM (SELECT DISTINCT id3 FROM source_table) AS t1
            CROSS JOIN (SELECT COLUMN_NAME AS col FROM INFORMATION_SCHEMA.COLUMNS 
                        WHERE TABLE_SCHEMA = 'dbo' 
                          AND TABLE_NAME = 'source_table' 
                          AND COLUMN_NAME LIKE 'value%') AS t2) +
           ' FROM source_table GROUP BY id1, id2;';

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

关键注意事项

  • 如果数据库不允许列名包含=,可以把生成的列名改成value1_id3_1这类格式,只需要修改拼接列名的部分即可。
  • 动态SQL方案会自动识别所有value列和id3取值,后续新增value列或id3值时,不需要修改代码,完全满足“支持任意数量value列”的需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 18:53:13