【问题标题】:How to convert rows to columns in PostgreSQL?如何在 PostgreSQL 中将行转换为列?
【发布时间】:2019-10-15 16:15:46
【问题描述】:

我有两个数据库表如下

site
id    |    name
1        Site One
2        Site Two
3        Site Three

asset_quantities
id   |   site_id   |  asset_type   | quantity
1           1             1              10
2           1             2              15
3           1             3              25
4           2             1              11
5           2             2              16
6           2             3              7
7           3             1              12
8           3             2              15
9           3             3              16

我想编写一个 SQL 查询,该查询将根据给定资产类型的“数量”列对结果进行排序 例如。我想根据所有站点中资产类型 1 的数量对结果进行降序排序。 对于这样的场景,我将如何构建 SQL 查询?

如果我想要所有站点中资产类型 1 的数量的降序结果,下面是我想要的示例结果

site_id  |   asset_type_1_qty   |   asset_type_2_qty  |  asset_type_3_qty 
   3                12                     15                  16
   2                11                     16                  7
   1                10                     15                  25

【问题讨论】:

  • 你能举个例子吗?我们会更清楚地了解您想要的。
  • Shawn - 我已经用我想要的示例结果更新了问题
  • 我已经给出了答案,希望对你有所帮助。

标签: sql postgresql sql-order-by jooq


【解决方案1】:

这是一个示例,它将在 sqlserver 中执行您想要的操作。您应该能够使其适应 postgres。

我有两个版本,一个对排序进行硬编码,第二个使用参数来决定排序依据。

先在临时表变量中创建数据

declare @s table(id int, site_id int, asset_type int, quantity int)
insert @s values (1,1,1,10)
,(2,1,2,15)
,(3,1,3,25)
,(4,2,1,11)
,(5,2,2,16)
,(6,2,3,7)
,(7,3,1,12)
,(8,3,2,15)
,(9,3,3,16)

固定列排序的第一个版本

select site_id, 
    max(case when asset_type=1 then quantity else 0 end) as q1,
    max(case when asset_type=2 then quantity else 0 end) as q2,
    max(case when asset_type=3 then quantity else 0 end) as q3
from @s 
group by site_id
order by 2 desc

带参数列排序的第二版

declare @assetsort int;
set @assetsort=3

select * from (
    select site_id, 
        max(case when asset_type=1 then quantity else 0 end) as q1,
        max(case when asset_type=2 then quantity else 0 end) as q2,
        max(case when asset_type=3 then quantity else 0 end) as q3
    from @s 
    group by site_id
) q 
order by 
    case @assetsort when 1 then q1 when 2 then q2 when 3 then q3 end desc

【讨论】:

    【解决方案2】:

    好的,这很容易实现,只需使用self join,如下:

    select
        s1.site_id,
        s1.quantity as asset_type_1_qty,
        s2.quantity as asset_type_2_qty,
        s3.quantity as asset_type_3_qty
    from
        asset_quantities s1
    join
        asset_quantities s2 on s1.site_id=s2.site_id and s1.asset_type=1 and s2.asset_type=2
    join
        asset_quantities s3 on s1.site_id=s3.site_id and s3.asset_type=3
    order by
        s1.quantity desc
    

    好的,有一个叫crosstab的函数可以将行转换为列,我认为它可以完全满足您的需求,如下所示:

    postgres=# select * from asset_quantities;
     id | site_id | asset_type | quantity 
    ----+---------+------------+----------
      1 |       1 |          1 |       10
      2 |       1 |          2 |       15
      3 |       1 |          3 |       25
      4 |       2 |          1 |       11
      5 |       2 |          2 |       16
      6 |       2 |          3 |        7
      7 |       3 |          1 |       12
      8 |       3 |          2 |       15
      9 |       3 |          3 |       16
    (9 rows)
    
    postgres=# select * from crosstab(
    'select
        site_id,
        asset_type,
        quantity
    from
        asset_quantities order by site_id,asset_type'
    ) as result(site_id int,asset_type_1_qty int,asset_type_2_qty int,asset_type_3_qty int);
     site_id | asset_type_1_qty | asset_type_2_qty | asset_type_3_qty 
    ---------+------------------+------------------+------------------
           1 |               10 |               15 |               25
           2 |               11 |               16 |                7
           3 |               12 |               15 |               16
    (3 rows)
    

    哦对了,在使用crosstab函数之前,你应该在PostgreSQL中执行下面的子句,否则会报错。

    CREATE EXTENSION tablefunc;
    

    【讨论】:

    • 谢谢肖恩。有没有办法使查询通用,以便它可以容纳任意数量的资产类型?目前我只有 3 种资产类型,但用户将来最多可以添加 30 种。在这种情况下,我不想加入 30 次,因为这会显着降低查询速度。
    • 好吧,让我想想再给你一个更完美的答案,等几分钟。
    • @user1066568 我已经更新了我的答案,我想它会完美地解决你的问题。祝你好运!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-11
    • 2012-12-14
    • 1970-01-01
    • 2017-05-23
    • 1970-01-01
    相关资源
    最近更新 更多