1

我想在 PostgreSQL 中将单行转换为多行,其中一些列被删除。这是当前输出的示例:

name | st | ot | dt |
-----|----|----|----|
Fred | 8  | 2  | 3  |
Jane | 8  | 1  | 0  |
Samm | 8  | 0  | 6  |  
Alex | 8  | 0  | 0  |  

使用以下查询:

SELECT
   name, st, ot, dt
FROM
   times;

这就是我想要的:

name |  t | val |
-----|----|-----|
Fred | st |  8  |
Fred | ot |  2  |
Fred | dt |  3  |
Jane | st |  8  |
Jane | ot |  1  |
Samm | st |  8  |
Samm | dt |  6  |
Alex | st |  8  |

如何修改查询以获得上述所需的输出?

4

3 回答 3

3
SELECT
  times.name, x.t, x.val
FROM
  times cross join lateral (values('st',st),('ot',ot),('dt',dt)) as x(t,val)
WHERE
  x.val <> 0;
于 2017-07-19T22:12:21.147 回答
1

核心问题是枢轴/交叉表操作的逆向。有时称为“反枢轴”

基本上,Abelisto 的查询是 Postgres 9.3 或更高版本的方式。有关的:

可能希望LEFT JOIN LATERAL ... ON u.val <> 0在结果中包含没有有效值的名称(并稍微缩短语法)。

如果您有多个值列(或不同的列列表),您可能希望使用一个函数来自动构建和执行查询:

CREATE OR REPLACE FUNCTION f_unpivot_columns(VARIADIC _cols text[])
  RETURNS TABLE(name text, t text, val int) AS
$func$
BEGIN
   RETURN QUERY EXECUTE (
   SELECT
     'SELECT t.name, u.t, u.val
      FROM   times t
      LEFT   JOIN LATERAL (VALUES '
          || string_agg(format('(%L, t.%I)', c, c), ', ')
          || ') u(t, val) ON (u.val <> 0)'
   FROM   unnest(_cols) c
   );
END
$func$  LANGUAGE plpgsql;

称呼:

SELECT * FROM f_unpivot_times_columns(VARIADIC '{st, ot, dt}');

或者:

SELECT * FROM f_unpivot_columns('ot', 'dt');

列名作为字符串文字提供,并且必须是正确的(区分大小写!)拼写,没有额外的双引号。看:

dbfiddle在这里

与更多示例和解释相关:

于 2017-07-20T02:43:46.163 回答
0

单程:

with times(name , st , ot , dt) as(
select 'Fred',8  , 2  , 3  union all
select 'Jane',8  , 1  , 0  union all
select 'Samm',8  , 0  , 6  union all
select 'Alex',8  , 0  , 0  
)

select name, key as t, value::int  from 
(
    select name, json_build_object('st' ,st , 'ot',ot, 'dt',dt) as j
    from times
) t
join lateral json_each_text(j)
on true
where value <> '0'
-- order by name, case when key = 'st' then 0 when key = 'ot' then 1 when key = 'dt' then 2 end
于 2017-07-19T22:29:43.617 回答