使用 PostgreSQL 的交叉表查询中的计数和总和无效

Invalid count and sum in cross tab query using PostgreSQL

我使用的是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        

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

第一步,您需要获得正确的金额。这会很快达到目的:

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

这将是您的结果:

NAME    TOTALSALES  TOTALAMOUNT
P2      3           6860
P1      3           2700

您将通过这种方式获得 Products/City:

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.

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

  • PostgreSQL Crosstab Query

totalsalestotalamount 的意外值表示每个 name 组第一行的值。 "Extra" 列就是这样处理的。详情:

  • Pivot on Multiple Columns using Tablefunc

要根据 name、运行 window 函数对聚合函数求和。详情:

  • Get the distinct sum of a joined table column

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
   );

更好的是,提供一个静态集作为第二个参数。输出列是硬编码的,动态生成数据列可能不可靠。如果你在新城市的另一行,这会中断。
通过这种方式,您还可以根据需要对列进行排序。只需保持输出列和第二个参数同步。