【问题标题】:Invalid count and sum in cross tab query using PostgreSQL使用 PostgreSQL 的交叉表查询中的计数和总和无效
【发布时间】:2015-04-18 04:37:32
【问题描述】:

我使用的是 PostgreSQL 9.3 版本的数据库。

我有一种情况,我想计算产品销售数量并计算产品数量,并且还想在产品销售的列中显示城市。

示例

设置

create table products (
 name varchar(20),
 price integer,
 city varchar(20)
);

insert into products values
   ('P1',1200,'London'),
   ('P1',100,'Melborun'),
   ('P1',1400,'Moscow'),
   ('P2',1560,'Munich'),
   ('P2',2300,'Shunghai'),
   ('P2',3000,'Dubai');

交叉表查询

select * from crosstab (
        'select  name,count(*),sum(price),city,count(city)
         from products
         group by  name,city
         order by  name,city
         '
        ,           
        'select distinct city from products order by 1'
     ) 
     as tb (
         name varchar(20),TotalSales bigint,TotalAmount bigint,London bigint,Melborun bigint,Moscow bigint,Munich bigint,Shunghai bigint,Dubai bigint
     );

输出

name    totalsales  totalamount     london     melborun    moscow      munich    shunghai    dubai  
---------------------------------------------------------------------------------------------------------
 P1         1           1200                       1          1            1 
 P2         1           3000          1                                               1         1

预期输出

name    totalsales  totalamount     london     melborun    moscow      munich    shunghai    dubai  
---------------------------------------------------------------------------------------------------------
 P1         3           2700           1          1           1               
 P2         3           6860                                               1          1         1        

【问题讨论】:

    标签: sql postgresql postgresql-9.3 crosstab window-functions


    【解决方案1】:

    您的第一个错误似乎很简单。根据crosstab() 函数的第二个参数,'Dubai' 必须作为第一个城市(按城市排序)。详情:

    totalsalestotalamount 的意外值表示每个 name 组的第一行中的值。 “额外”列被这样处理。详情:

    要获得每个 name 的总和,请在聚合函数上运行窗口函数。详情:

    select * from crosstab (
       'select name
              ,sum(count(*))   OVER (PARTITION BY name)
              ,sum(sum(price)) OVER (PARTITION BY name)
              ,city
              ,count(city)
        from   products
        group  by name,city
        order  by name,city
        '
    --  ,'select distinct city from products order by 1' -- replaced
        ,$$SELECT unnest('{Dubai,London,Melborun
                          ,Moscow,Munich,Shunghai}'::varchar[])$$
    ) AS tb (
        name varchar(20), TotalSales bigint, TotalAmount bigint
       ,Dubai bigint
       ,London bigint
       ,Melborun bigint
       ,Moscow bigint
       ,Munich bigint
       ,Shunghai bigint
       );
    

    更好的是,提供一个静态集作为第二个参数。输出列是硬编码的,动态生成数据列可能不可靠。如果您与新城市的另一排,这将打破。
    这样,您还可以根据需要对列进行排序。只需保持输出列和第二个参数同步即可。

    【讨论】:

    • 快完成了。但是当我插入更多行时,例如insert into products select * from products; TotalSales 不变。
    • @MAK:不变?你能澄清一下吗?
    • 我的意思是说 TotalSales 对于产品 p1 必须为 6,对于 p2 必须为 6。当我再次插入相同的六行时。
    • 非常感谢。一如既往,你很棒。
    • 动态列!我正在使用string_agg 生成逗号分隔的列列表字符串,然后为这些字符串添加数据类型并在crosstab 中使用它们。我这样做是对的还是有更好的方法?
    【解决方案2】:

    老实说,我认为您的数据库需要进行一些剧烈的规范化,并且您在几列(每个城市名称各一列)中的结果不是我自己会做的事情。 不过,如果你想坚持下去,你可以这样做。

    第一步,您需要获取正确的金额。这会很快解决问题:

    select name, count(1) totalsales, sum(price) totalAmount 
    from products 
    group by name;
    

    这将是你的结果:

    NAME    TOTALSALES  TOTALAMOUNT
    P2      3           6860
    P1      3           2700
    

    您将通过这种方式获得产品/城市:

    select name, city, count(1) totalCityName 
    from products 
    group by name, city 
    order by name, city;
    

    这个结果:

    NAME    CITY        TOTALCITYNAME
    P1      London      1
    P1      Melborun    1
    P1      Moscow      1
    P2      Dubai       1
    P2      Munich      1
    P2      Shunghai    1
    

    如果你真的想要每个城市的专栏,你可以这样做:

    select name,
    count(1) totalsales, 
    sum(price) totalAmount, 
    (select count(1) 
        from Products a 
        where a.City = 'London' and a.name = p.name) London,
    ...
    from products p 
    group by name;
    

    但我不会推荐它! 结果是这样的:

    NAME    TOTALSALES  TOTALAMOUNT LONDON ...
    P1      3           2700        1
    P2      3           6860        0
    

    Demonstration here.

    【讨论】:

      猜你喜欢
      • 2019-05-25
      • 1970-01-01
      • 2011-08-12
      • 2011-03-01
      相关资源
      最近更新 更多