2014-04-07 47 views
2

我試圖將應用程序從MySQL 5.6移植到PostgreSQL 9.2原始應用程序使用一個視圖,我已經設法至少運行但查詢時間很糟糕。提高PostgreSQL中嵌套的查詢性能不在

我想知道PostgreSQL中的最佳方法來優化「不在」查詢。

我的第一個想法是創建一個臨時表,但由於這是一個視圖,我不認爲這是一個選項。

create VIEW ready_ports AS 
    SELECT ports.id AS id, 
      ports.run AS run, 
      ports.name AS name, 
      ports.pkgname AS pkgname, 
      ports.version AS version, 
      ports.description AS description, 
      ports.license AS license, 
      ports.www AS www, 
      ports.status AS status, 
      ports.updated AS updated, 
      (SELECT count(0) AS COUNT 
      FROM depends 
      WHERE depends.dependency = ports.id) AS priority 
    FROM ports 
    WHERE (ports.status = 'untested' and 
      (not(ports.id in 
       (SELECT locks.port AS port 
        FROM locks 
        WHERE locks.port = ports.id) 
       ) 
     ) and 
      (
      (not(ports.id in (SELECT depends.port AS port 
           FROM depends 
           WHERE depends.port = ports.id))) or 
      (not(ports.id in 
        (SELECT depends.port AS port 
        FROM depends 
        WHERE ((not(depends.dependency in 
        (SELECT ports.id AS dep_id 
         FROM ports 
         WHERE (ports.id = depends.dependency 
          and (ports.status = 'pass' 
            or ports.status = 'warn') 
          ) 
        ))) or 
    depends.dependency in 
    (SELECT locks.port AS port 
    FROM locks 
    WHERE locks.port = ports.id))))))) 
ORDER BY priority desc 
 
                QUERY PLAN             
---------------------------------------------------------------------------------------------------------------- 
Sort (cost=367498265655.68..367498265763.29 rows=43047 width=136) 
    Sort Key: ((SubPlan 1)) 
    -> Index Scan using ports_1_idx on ports (cost=0.00..367498259398.93 rows=43047 width=136) 
     Index Cond: ((status)::text = 'untested'::text) 
     Filter: ((NOT (SubPlan 2)) AND ((NOT (SubPlan 3)) OR (NOT (SubPlan 6)))) 
     SubPlan 1 
      -> Aggregate (cost=9.62..9.63 rows=1 width=0) 
       -> Index Only Scan using depends_dependency_idx on depends (cost=0.00..9.47 rows=60 width=0) 
         Index Cond: (dependency = public.ports.id) 
     SubPlan 2 
      -> Index Only Scan using locks_port_key on locks (cost=0.00..8.27 rows=1 width=4) 
       Index Cond: (port = public.ports.id) 
     SubPlan 3 
      -> Index Only Scan using depends_pkey on depends (cost=0.00..8.72 rows=14 width=4) 
       Index Cond: (port = public.ports.id) 
     SubPlan 6 
      -> Seq Scan on depends (cost=8.27..6399946.81 rows=1150079 width=4) 
       Filter: ((NOT (SubPlan 4)) OR (hashed SubPlan 5)) 
       SubPlan 4 
        -> Index Scan using ports_pkey on ports (cost=0.00..8.31 rows=1 width=4) 
         Index Cond: (id = public.depends.dependency) 
         Filter: (((status)::text = 'pass'::text) OR ((status)::text = 'warn'::text)) 
       SubPlan 5 
        -> Index Only Scan using locks_port_key on locks (cost=0.00..8.27 rows=1 width=4) 
         Index Cond: (port = public.ports.id) 
+0

請正確格式化查詢。 –

+0

@Denis定義「正確」 – rjzii

+0

@rob:就像現在是好的 - 或者至少是合理的。查看最初發布時的樣子:內嵌子查詢的一大塊。 –

回答

4

你可以嘗試NOT EXISTS &反連接的版本,NOT IN不能使用太多的索引(因爲NULL處理issues):

SELECT * 
FROM table1 
WHERE table1.id NOT IN (SELECT id FROM table2) 

-- vs NOT EXISTS 

SELECT * 
FROM table1 
WHERE NOT EXISTS (SELECT * FROM table2 WHERE table1.id = table2.id) 

-- vs anti-join 

SELECT * 
FROM table1 
LEFT JOIN table2 ON table1.id = table2.id 
WHERE table2.id IS NULL 
1

你需要重寫查詢使用連接:

select ... 
from ports 
left join locks ... 
left join depends ... 
where criteria 

這樣,你將會製作一套大套裝,這是三套套裝的結果,而不是半套套裝。

將計數移出您的視圖也將是一個優點。使用單獨的查詢或加入您的視圖來獲取該部分。 (在視圖中聚合很少是一個好主意,除了在報表中)。

2

我結束了使用連接和不存在查詢的組合,以獲得最終的工作查詢。

create VIEW ready_ports AS 
SELECT ports.id AS id, 
     ports.run AS run, 
     ports.name AS name, 
     ports.pkgname AS pkgname, 
     ports.version AS version, 
     ports.description AS description, 
     ports.license AS license, 
     ports.www AS www, 
     ports.status AS status, 
     ports.updated AS updated, 
     (SELECT count(0) AS COUNT 
     FROM depends 
     WHERE depends.dependency = ports.id) AS priority 
FROM ports 
LEFT JOIN locks on locks.port = ports.id 
LEFT JOIN depends on depends.port = ports.id 
WHERE ports.status = 'untested' and locks.id is null and 
     (depends.port is null or 
     not exists 
       (SELECT depends.port AS port 
       FROM depends WHERE ports.id = depends.port and not exists    
       (SELECT ports.id as dep_id 
        FROM ports 
        WHERE ports.id = depends.dependency and 
        (ports.status = 'pass' or ports.status = 'warn')) 
        or 
depends.dependency = locks.port)) 
ORDER BY priority desc, ports.name asc