【问题标题】:transform columns to rows将列转换为行
【发布时间】:2017-08-11 06:34:18
【问题描述】:

我有一张桌子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   |
+----+------+--------+

【问题讨论】:

    标签: sql postgresql unpivot


    【解决方案1】:

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

    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'
    

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

    【讨论】:

      【解决方案2】:

      您可以为此使用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)
      

      它只使用表一次,可以轻松扩展以容纳更多列。

      Demo

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

      【讨论】:

        【解决方案3】:

        我相信还有比这更经典的方法。

        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
        

        【讨论】:

        • 他有 30 列(他再次没有告诉我们)。更好的方法可能是使用交叉表。 @a_horse_with_no_name 可以解决这个问题。
        • 我现在实际上正在尝试交叉连接方法哈哈@TimBiegeleisen
        • Postgres 不支持 UNPIVOT。
        • @BennjoeMordeno - 你能看看 GurV 的评论并可能修复答案的第二部分吗?
        【解决方案4】:

        它不会根据用户的需要在 postgress 中工作。在 cmets 中提到它的时候看到了。

        我正在寻找一种方法来处理“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

        【讨论】:

        • 不是 SQL Server。
        猜你喜欢
        • 2015-04-25
        • 2011-05-15
        相关资源
        最近更新 更多