2017-03-21 42 views
1

我有数据,像这样的hstore:总结hstore BY

|brand|account|likes|views     | 
|-----|-------|-----|----------------------| 
|Ford |ford_uk|1 |"3"=>"100"   | 
|Ford |ford_us|2 |"3"=>"200", "5"=>"10" | 
|Jeep |jeep_uk|3 |"3"=>"300"   | 
|Jeep |jeep_us|4 |"3"=>"400", "5"=>"20" | 

我想应该能总结通过键hstores,按品牌划分:

|brand|likes|views     | 
|-----|-----|----------------------| 
|Ford |3 |"3"=>"300", "5"=>"10" | 
|Jeep |7 |"3"=>"700", "5"=>"20" | 

This answer为如何在没有GROUP BY的情况下提供了一个很好的解决方案。它适应这种情况给类似:

SELECT 
    sum(likes) AS total_likes, 
(SELECT hstore(array_agg(key), array_agg(value::text)) 
    FROM (
    SELECT s.key, sum(s.value::integer) 
    FROM (
     SELECT((each(views)).*) 
    ) AS s(key, value) 
    GROUP BY key 
) x(key, value)) AS total_views 
FROM my_table 
GROUP BY brand 

但是这给:

ERROR: subquery uses ungrouped column "my_table.views" from outer query

任何帮助表示赞赏!

回答

4

这是因为在group by查询中使用views列没有聚合函数。
非常快的解决方法:

with my_table(brand,account,likes,views) as (
    values 
    ('Ford', 'ford_uk', 1, '"3"=>"100"'::hstore), 
    ('Ford', 'ford_uk', 2, '"3"=>"200", "5"=>"10"'), 
    ('Jeep', 'jeep_uk', 3, '"3"=>"300"'::hstore), 
    ('Jeep', 'jeep_uk', 4, '"3"=>"400", "5"=>"20"')) 
SELECT 
    brand, 
    sum(likes) AS total_likes, 
(SELECT hstore(array_agg(key), array_agg(value::text)) 
    FROM (
    SELECT s.key, sum(s.value::integer) 
    FROM 
     unnest(array_agg(views)) AS h, --<< aggregate views according to the group by, then unnest it into the table 
     each(h) as s(key,value) 
    GROUP BY key 
) x(key, value)) AS total_views 
FROM my_table 
GROUP BY brand 

更新

你也可以为这样的任务创建aggregate

--drop aggregate if exists hstore_sum(hstore); 
--drop function if exists hstore_sum_ffunc(hstore[]); 
create function hstore_sum_ffunc(hstore[]) returns hstore language sql immutable as $$ 
    select hstore(array_agg(key), array_agg(value::text)) 
    from 
    (select s.key, sum(s.value::numeric) as value 
    from unnest($1) as h, each(h) as s(key, value) group by s.key) as t 
$$; 
create aggregate hstore_sum(hstore) 
(
    SFUNC = array_append, 
    STYPE = hstore[], 
    FINALFUNC = hstore_sum_ffunc, 
    INITCOND = '{}' 
); 

之后,你的查询会更简单和更 “规范” :

select 
    brand, 
    sum(likes) as total_likes, 
    hstore_sum(views) as total_views 
from my_table 
group by brand; 

更新2

即使没有create aggregate功能hstore_sum_ffunc可能是有用的:

select 
    brand, 
    sum(likes) as total_likes, 
    hstore_sum_ffunc(array_agg(views)) as total_views 
from my_table 
group by brand; 
+0

真是太爽了@Abelisto。非常感激。 –

1

如果您创建hstore的聚集,这变得更容易一点:

create aggregate hstore_agg(hstore) 
(
    sfunc = hs_concat(hstore, hstore), 
    stype = hstore 
); 

然后你可以这样做:

with totals as (
    select t1.brand, 
     hstore(k, sum(v::int)::text) as views 
    from my_table t1, each(views) x(k,v) 
    group by brand, k 
) 
select brand, 
     (select sum(likes) from my_table t2 where t1.brand = t2.brand) as likes, 
     hstore_agg(views) as views 
from totals t1 
group by brand; 

另一种选择是将联合相关子查询,这可能是缓慢的进入CTE:

with vals as (
    select t1.brand, 
     hstore(k, sum(v::int)::text) as views 
    from my_table t1, each(views) x(k,v) 
    group by brand, k 
), view_totals as (
    select brand, 
     hstore_agg(views) as views 
    from vals 
    group by brand 
), like_totals as (
    select brand, 
     sum(likes) as likes 
    from my_table 
    group by brand 
) 
select vt.brand, 
     lt.likes, 
     vt.views 
from view_totals vt 
    join like_totals lt on vt.brand = lt.brand 
order by brand;