1

I have data with an hstore like this:

|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" |

I would like to be able to sum the hstores by key, grouped by brand:

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

This answer gives a good solution for how to do this without a GROUP BY. Adapting it to this situation gives something like:

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

However this gives:

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

Any help appreciated!

2 Answers 2

4

It is because of using views column without aggregate function in the group by query.
Very quick workaround:

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

Update

Also you can to create the aggregate for such tasks:

--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 = '{}'
);

After that your query will be simpler and more "canonical":

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

Update 2

Even without create aggregate the function hstore_sum_ffunc could be useful:

select
  brand, 
  sum(likes) as total_likes,
  hstore_sum_ffunc(array_agg(views)) as total_views
from my_table
group by brand;
Sign up to request clarification or add additional context in comments.

Comments

1

If you create an aggregate for hstore, this gets a bit easier:

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

Then you can do this:

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;

Another option is to move the co-related sub-query which might be slow into a 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;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.