将列转换为行

transform columns to rows

我有一个 table table1 如下所示

+----+------+------+------+------+------+
| id | loc  | val1 | val2 | val3 | val4 |
+----+------+------+------+------+------+
|  1 | loc1 |   10 | 190  | null |   20 |
|  2 | loc2 |   20 | null | 10   |   10 |
+----+------+------+------+------+------+

需要将 val1 到 val4 组合成一个新列 val,每个列占一行,以便输出如下所示。

注意:-我的数据有 val1 到 val30 -> 即。每行 30 列需要转换为行。

+----+------+--------+
| id | loc  |  val   |
+----+------+--------+
|  1 | loc1 |   10   |
|  1 | loc1 |   190  |
|  1 | loc1 |   null |
|  1 | loc1 |   20   |
|  2 | loc2 |   20   |
|  2 | loc2 |   null |
|  2 | loc2 |   10   |
|  2 | loc2 |   10   |
+----+------+--------+

我敢肯定还有比这更经典的方法。

SELECT * FROM (
select id, loc, val1 as val from #t a
UNION ALL
select id, loc, val2 as val from #t a
UNION ALL
select id, loc, val3 as val from #t a
UNION ALL
select id, loc, val4 as val from #t a
) x
order by ID

这是我对 unpivot 的尝试,但无法获取空值,也许对空值执行连接?反正我还是会试试

SELECT *
FROM (
SELECT * FROM #t
) main
UNPIVOT (
    new_val 
    FOR val IN (val1, val2, val3, val4) 
) unpiv

为此,您可以使用 cross joingenerate_series

select
    id,
    loc,
    case x.i
        when 1 then val1 
        when 2 then val2
        . . .
    end as val
from t 
cross join generate_series(1, 4) x (i)

它仅使用一次 table,并且可以轻松扩展以容纳更多列。

Demo

注意:在接受的答案中,第一种方法多次读取 table(与要取消透视的列一样多),第二种方法方法是错误的,因为在 postgresql 中没有 UNPIVOT。

它不会根据用户需要在后期工作。看到评论里提到了。

我正在寻找一种方法来处理 "NULL"

select p.id,p.loc,CASE WHEN p.val=0 THEN NULL ELSE p.val END AS val
from 
(
    SELECT id,loc,ISNULL(val1,0) AS val1,ISNULL(val2,0) AS val2,ISNULL(val3,0) AS val3,ISNULL(val4,0) AS val4
    FROM Table1
)T
unpivot
(
  val
  for locval in(val1,val2,val3,val4)
)p

Test

编辑:

我方的最佳解决方案:

select a.id,a.loc,ex.val
from (select 'val1' as [over] union all select 'val2' union all select 'val3'
        union all select 'val1' ) pmu
cross join (select id,loc from Table1) as a
left join
Table1 pt
unpivot
(
 [val]
 for [over] in (val1, val2, val3, val4)
) ex
on pmu.[over] = ex.[over] and
   a.id = ex.id

Test

您可以使用横向连接将列转换为行:

SELECT a.id,a.loc,t.vals
FROM table1 a,
unnest(ARRAY[a.val1,a.val2,a.val3,a.val4]) t(vals);

如果你想动态添加列:

CREATE OR REPLACE FUNCTION columns_to_rows(
 out id integer,
 out loc text,
 out vals integer
)
RETURNS SETOF record AS
$body$
DECLARE

columns_to_rows text;

BEGIN

 SELECT string_agg('a.'||attname, ',') into columns_to_rows
 FROM pg_attribute
 WHERE attrelid = 'your_table'::regclass AND --table name
 attnum > 0 and --get just the visible columns
 attname <> all (array [ 'id', 'loc' ]) AND --exclude some columns
 NOT attisdropped ; --column is not dropped

 RETURN QUERY
 EXECUTE format('SELECT a.id,a.loc,t.vals
 FROM your_table a, 
 unnest(ARRAY[%s]) t(vals)',columns_to_rows);

end;
$body$
LANGUAGE 'plpgsql'

查看此 link 了解更多详情:Columns to rows