2011-11-22 40 views
2

我有一個查詢,看起來有點像這樣(注:由Hibernate生成實際的查詢,是一個比較複雜一點):爲什麼我的子查詢甚至對過濾的行執行?

select * from outage_revisions orev 
join outages o 
    on orev.outage=o.id 
    where o.observed_end is null 
    and orev.observation_date = 
     (select max(observation_date) 
      from outage_revisions orev2 
      where orev2.observation_date <= '2011-11-21 00:00:00' 
      and orev2.outage = orev.outage); 

這個查詢運行速度非常慢(約15分鐘)。但是,如果我用子查詢取出where子句的部分,它幾乎立即(約83毫秒)回來,只有大約14行。

此外,子查詢本身是非常快的(約31毫秒):

select max(observation_date) from outage_revisions orev2 
where orev2.observation_date <= '2011-11-21 00:00:00' 
and orev2.outage = 1 

我的問題是這樣的:如果只有14行從全查詢返回不包括子查詢過濾器,爲什麼不加入子查詢減慢查詢這麼多?子查詢不應該添加最多大約31 * 14毫秒?

下面是完整的查詢計劃:

Nested Loop (cost=0.00..71078813.16 rows=1 width=115) 
    -> Seq Scan on outagerevisions orev (cost=0.00..71077624.67 rows=284 width=79) 
     Filter: (observationdate = (SubPlan 2)) 
     SubPlan 2 
      -> Result (cost=1250.56..1250.57 rows=1 width=0) 
       InitPlan 1 (returns $1) 
        -> Limit (cost=0.00..1250.56 rows=1 width=8) 
         -> Index Scan Backward using idx_observationdate on outagerevisions orev2 (cost=0.00..2501.12 rows=2 width=8) 
           Index Cond: (observationdate <= '2011-11-21 00:00:00'::timestamp without time zone) 
           Filter: ((observationdate IS NOT NULL) AND (outage = $0)) 
    -> Index Scan using outages_pkey on outages o (cost=0.00..4.17 rows=1 width=36) 
     Index Cond: (o.id = orev.outage) 
     Filter: (o.observedend IS NULL) 
+0

每張表中的總行數是多少,您是否會執行正確的索引? –

+0

@aF大約39000行in'outage'和大約60000行in'outage_revisions'。我有一個關於'observation_date'的索引,當然還有所有的主鍵。 –

+0

你試過拆分查詢嗎?首先選擇這些值,然後刪除不需要的值。 –

回答

3

我的猜測是PostgreSQL的只是把它如何執行查詢的一個糟糕的選擇。雖然在執行相關子查詢之前,它應該縮小到9行似乎很明顯,但它可能不會這樣做,所以子查詢必須運行6萬次。雖然它在做什麼,它也能夠跟蹤其行將繼續下一步,等

這裏有一些其他的方式,你可以嘗試把它寫:

SELECT 
    <column list> 
FROM 
    Outage_Revisions OREV 
JOIN Outages O ON 
    OREV.outage = O.id 
LEFT OUTER JOIN Outage_Revisions OREV2 ON 
    OREV2.outage = OREV.outage AND 
    OREV2.observation_date <= '2011-11-21 00:00:00' AND 
    OREV2.observation_date > OREV.observation_date 
WHERE 
    O.observed_end IS NULL AND 
    OREV2.outage IS NULL 

或 (假設PostgreSQL和Hibernate支持加入子查詢)

SELECT 
    <column list> 
FROM 
    Outage_Revisions OREV 
JOIN Outages O ON 
    OREV.outage = O.id 
JOIN (SELECT OREV2.outage, MAX(OREV2.observation_date) AS max_observation_date 
     FROM Outage_Revisions OREV2 
     WHERE OREV2.observation_date <= '2011-11-21 00:00:00' 
     GROUP BY OREV2.outage) SQ ON 
    SQ.outage = OREV.outage AND 
    SQ.max_observation_date = OREV.observation_date 
WHERE 
    O.observed_end IS NULL 

您可以在最後一個查詢中使用連接的順序。

相關問題