2017-04-20 92 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)輸出