2017-04-20 95 views
0

我们在我们的rails项目中使用ancestry gem。表中有大约800个类别:递归postgres SQL优化

db => SELECT id, ancestry FROM product_categories LIMIT 10; 

id | ancestry 
-----+------------- 
399 | 3 
298 | 8/292/294 
12 | 3/401/255 
573 | 349/572 
707 | 7/23/89/147 
201 | 166/191 
729 | 5/727 
84 | 7/23 
128 | 7/41/105 
405 | 339 

(10 rows) 

ancestry字段代表记录的“路径”。我需要的是建立一个地图{CATEGORY_ID => [... all_subtree_ids ...]}

我解决了这个通过使用子查询是这样的:

SELECT id, 
    (
    SELECT array_agg(id) 
    FROM product_categories 
    WHERE (ancestry LIKE CONCAT(p.id, '/%') OR 
      ancestry = CONCAT(p.ancestry, '/', p.id, '') OR 
      ancestry = (p.id) :: TEXT) 
) categories 
FROM product_categories p 
ORDER BY id 

导致

1 | {17,470,32,29,15,836,845,837} 
2 | {37,233,231,205,107,109,57,108,28,58, ...} 

但问题是这个查询运行大约100ms,我不知道是否有一种方法来优化它使用WITH recursive?我是新手,所以我的查询只是挂postgres :(

** ========= UPD ========= ** 接受AlexM答案为最快,但如果任何人有兴趣,这里的递归解决方案:

WITH RECURSIVE a AS 
(SELECT id, id as parent_id FROM product_categories 
UNION all 
SELECT pc.id, a.parent_id FROM product_categories pc, a 
WHERE regexp_replace(pc.ancestry, '^(\d{1,}/)*', '')::integer = a.id) 

SELECT parent_id, sort(array_agg(id)) as children FROM a WHERE id <> parent_id group by parent_id order by parent_id; 

回答

1

尝试这种方法,我认为它应该比嵌套查询快得多:

WITH product_categories_flat AS (
    SELECT id, unnest(string_to_array(ancestry, '/')) as parent 
    FROM product_categories 
) 
SELECT parent as id, array_agg(id) as children 
FROM product_categories_flat 
GROUP BY parent 
+0

真棒,谢谢 – Eugene

0

赔率是联接更快:

SELECT p1.id, 
     p2.array_agg(id) 
FROM product_categories p 
    JOIN product_categories p2 
     ON p2.ancestry LIKE CONCAT(p1.id, '/%') 
     OR p2.ancestry = CONCAT(p1.ancestry, '/', p1.id) 
     OR p2.ancestry = p1.id::text) 
GROUP BY p1.id 
ORDER BY p1.id; 

但说些什么明确的,你得看EXPLAIN (ANALYZE, BUFFERS)输出