Invalid count and sum in cross tab query using PostgreSQL

你离开我真会死。 提交于 2019-12-01 08:39:23
Erwin Brandstetter

Your first mistake seems to be simple. According to the 2nd parameter of the crosstab() function, 'Dubai' must come as first city (sorted by city). Details:

The unexpected values for totalsales and totalamount represent values from the first row for each name group. "Extra" columns are treated like that. Details:

To get sums per name, run window functions over your aggregate functions. Details:

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

Better yet, provide a static set as 2nd parameter. Output columns are hard coded, it may be unreliable to generate data columns dynamically. If you a another row with a new city, this would break.
This way you can also order your columns as you like. Just keep output columns and 2nd parameter in sync.

RGPT

Honestly I think your database needs some drastic normalization and your results in several columns (one for each city name) is not something I would do myself. Nevertheless if you want to stick to it you can do it this way.

For the first step you need get the correct amounts. This would do the trick quite fast:

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

This will be your result:

NAME    TOTALSALES  TOTALAMOUNT
P2      3           6860
P1      3           2700

You would get the Products/City this way:

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

This result:

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

If you really would like a column per city you could do something like:

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;

But I would not recommend it!!! This would be the result:

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

Demonstration here.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!