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

如何不使用UNION将SQL表中指定列转换为行?

问题描述

现有Orderdetails表结构如下:

-----------------------------------------------------------------
| ID |  ItemName | OldValue | newValue |  OrderId  | sequenceNo
-----------------------------------------------------------------
| 1 |  Item1   |   1     | 1.5     |  SO2   |   6
| 2 |  Item2   |   4     | 6       |  SO2   |   4
| 3 |  Item3   |   3     | 68      |  SO2    |  9 
------------------------------------------------------------------

需要编写查询语句将OldValue列数据转为新行,输出结果如下:

ItemName | allValues   |OrderId  | sequenceNo
----------------------------------------------
Item1   |   1          |  SO2   |   0
Item2   |   4          |  SO2   |   0
Item3   |   3          |  SO2   |   0
Item1   |   1.5        |  SO2   |   6
Item2   |   6          |  SO2   |   4
Item3   |   68         |  SO2   |   9
-----------------------------------------------

目前已通过UNION实现需求,代码如下:

select itemName , oldValue as allValues , OrderId, 0 as sequenceNo from Orderdetails
UNION
select itemName , newValue as allValues , OrderId, sequenceNo from Orderdetails

请问是否存在不使用UNION的更优实现方式?


非UNION的实现方案

当然有,不同SQL方言有原生的列转行方案,这些方法大多只需扫描一次原表,比UNION(尤其是带去重的UNION)性能更优,代码逻辑也更紧凑。以下是主流数据库的实现方式:

1. SQL Server / Azure SQL(T-SQL环境)

使用CROSS APPLY VALUES展开多列到多行,是这类场景下性能最优的方案之一:

SELECT 
    od.ItemName,
    v.allValues,
    od.OrderId,
    v.sequenceNo
FROM Orderdetails od
CROSS APPLY (
    VALUES 
        (od.OldValue, 0),
        (od.NewValue, od.sequenceNo)
) v(allValues, sequenceNo)
ORDER BY v.sequenceNo, od.ItemName;

2. PostgreSQL / BigQuery(支持数组与UNNEST)

通过数组包装目标列,再用UNNEST展开,配合序号标记区分新旧值:

-- PostgreSQL 示例
SELECT 
    od.ItemName,
    unnest_data.allValues,
    od.OrderId,
    CASE unnest_data.rn 
        WHEN 1 THEN 0 
        ELSE od.sequenceNo 
    END AS sequenceNo
FROM Orderdetails od
CROSS JOIN UNNEST(ARRAY[od.OldValue, od.NewValue]) WITH ORDINALITY AS unnest_data(allValues, rn)
ORDER BY sequenceNo, od.ItemName;

-- BigQuery 示例
SELECT 
    od.ItemName,
    allValues,
    od.OrderId,
    IF(offset = 0, 0, od.sequenceNo) AS sequenceNo
FROM Orderdetails od,
UNNEST([od.OldValue, od.NewValue]) AS allValues WITH OFFSET
ORDER BY sequenceNo, od.ItemName;

3. MySQL 8.0+

利用JSON_TABLE解析包含新旧值的JSON数组,实现行转列:

SELECT 
    od.ItemName,
    jt.allValues,
    od.OrderId,
    CASE jt.rn 
        WHEN 1 THEN 0 
        ELSE od.sequenceNo 
    END AS sequenceNo
FROM Orderdetails od
JOIN JSON_TABLE(
    JSON_ARRAY(od.OldValue, od.NewValue),
    '$[*]' COLUMNS (
        allValues DECIMAL(10,2) PATH '$',
        rn INT ORDINALITY
    )
) jt
ORDER BY sequenceNo, od.ItemName;

4. Oracle

使用CONNECT BY生成行号,再映射新旧值:

SELECT 
    od.ItemName,
    CASE level 
        WHEN 1 THEN od.OldValue 
        ELSE od.NewValue 
    END AS allValues,
    od.OrderId,
    CASE level 
        WHEN 1 THEN 0 
        ELSE od.sequenceNo 
    END AS sequenceNo
FROM Orderdetails od
CONNECT BY level <= 2
    AND PRIOR od.ID = od.ID
    AND PRIOR sys_guid() IS NOT NULL -- 防止循环生成数据
ORDER BY sequenceNo, od.ItemName;

方案优势

  • 仅扫描一次原表,避免了UNION需要两次扫描+合并/去重的额外开销;
  • 转换逻辑集中在一处,代码可读性和维护性更强。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 08:01:20