【问题标题】:Convert one row into multiple rows with fewer columns将一行转换为列数更少的多行
【发布时间】:2017-12-25 08:26:15
【问题描述】:

我想在 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  |

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

【问题讨论】:

  • 您想在结果中包含名称,其中所有值列都是0 - 其中一行在tval 中包含NULL?
  • 那么你有答案了吗?

标签: sql postgresql unpivot lateral


【解决方案1】:
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;

【讨论】:

    【解决方案2】:

    一种方式:

    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
    

    【讨论】:

    • 本来也打算建议 LATERAL 示例,但您获得了第一个 :) 不过,您不需要构建任何 json 对象并对其进行解析。简单的.. FROM times t, LATERAL ( VALUES ('st', t.st), ('ot', t.ot), ('dt', t.dt) ) c(t, val) 可以解决问题,因为您可以在 LATERAL 中引用times 表。
    • @ KristoMägi - 是的,你是对的。刚出现在我脑海中的第一件事就是使用 json,可能不是最佳方式
    • 另一条评论:使用 CTE 构建静态数据集时,您不需要 SELECT .. UNION ALL,使用 VALUES 就足够了:...AS ( VALUES ( 'Fred', 8, 2, 3 ), ( 'Jane', 8, 1, 0 ), ( 'Samm', 8, 0, 6 ), ( 'Alex', 8, 0, 0 ) ).. :)
    【解决方案3】:

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

    基本上,Abelisto's query 是 Postgres 9.3 或更高版本的方式。相关:

    可能想使用LEFT JOIN LATERAL ... ON u.val &lt;&gt; 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 here

    相关的更多例子和解释:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-12-01
      • 2022-11-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多