2015-09-24 3 views
0

У меня есть таблица, в которой хранится поле JSONB (data), которое содержит данные, подобные Facebook. Структура данных:Агрегат над массивом JSONB Эффективно

- 
id   | 9403 
kind  | 'likes' 
data  | [{ id: "1", name: "Pluto", category: "Planet"}, { id: "2", name: "Saturn", category: "Planet" }] 
- 
id   | 9403 
kind  | 'likes' 
data  | [{ id: "2", name: "Neptune", category: "Planet"}, { id: "3", name: "Mars", category: "Planet" }] 

цель состоит в том, чтобы написать запрос, который агрегирует по категориям верхний Н (5) любит каждую категорию. У меня есть следующий подзапрос, который я не уверен, как оптимизировать (с индексами или путем повторного написания). Цель состоит в том, чтобы получить группировку счетчиков для имен и категорий, чтобы они могли быть ранжированы. Я начинаю с простой задачей эффективно выбирая N самым популярным:

SELECT 
likes.entry->>'name' AS name, 
likes.entry->>'category' AS category, 
COUNT(*) AS count 
FROM (SELECT json_array_elements(metadata.data::JSON) AS entry FROM metadata WHERE metadata.kind = 'likes') AS likes 
GROUP BY name, category 
ORDER BY count DESC 
LIMIT 5 

Этого запрос уже занимает более 5 секунд для запуска (наклеено объяснить/проанализировать):

Limit (cost=39971.07..39971.07 rows=5 width=32) (actual time=5468.952..5468.954 rows=5 loops=1) 
    -> Sort (cost=39971.07..39971.17 rows=200 width=32) (actual time=5468.952..5468.954 rows=5 loops=1) 
     Sort Key: (count(*)) 
     Sort Method: top-N heapsort Memory: 25kB 
     -> HashAggregate (cost=39969.61..39970.41 rows=200 width=32) (actual time=5241.143..5376.502 rows=392515 loops=1) 
       Group Key: (likes.entry ->> 'name'::text), (likes.entry ->> 'category'::text) 
       -> Subquery Scan on likes (cost=0.00..34491.46 rows=3652100 width=32) (actual time=0.104..4552.531 rows=880073 loops=1) 
        -> Seq Scan on metadata (cost=0.00..19883.06 rows=3652100 width=703) (actual time=0.097..2146.678 rows=880073 loops=1) 
          Filter: ((kind)::text = 'likes'::text) 
          Rows Removed by Filter: 90145 

Могу ли я как-то реорганизовать это быстрее или добавить некоторые индексы без использования материализованного представления? Я попытался добавить следующее (бесполезный) индекс:

CREATE INDEX index_metadata_on_likes_raw ON metadata USING gin(data) WHERE (kind = 'likes'); 
CREATE INDEX index_metadata_on_likes_targeted ON metadata ((data ->> 'name'), (data ->> 'category')) WHERE (kind = 'likes'); 

ответ

0

Может вы попробовать:

select name, category, COUNT(*) AS count from(
    SELECT jsonb_array_elements(test.data::JSONB)->>'name' as name, jsonb_array_elements(test.data::JSONB)->>'category' as category FROM test WHERE test.kind = 'likes') a 
GROUP BY name, category 
ORDER BY count DESC 
LIMIT 5; 
Смежные вопросы