2016-03-04 71 views
1

我有一個複雜的查詢,需要總共4個表中的字段。內部連接導致查詢花費比預期更長的時間。我已經運行的EXPLAIN語句,其視覺效果連接下面:如何處理多個連接

EXPLAIN Statement output

這是我的查詢:

SELECT 
    pending_corrections.corrected_plate , pending_corrections.seenDate 
FROM 
    (pending_corrections 
    INNER JOIN cameras ON pending_corrections.camerauid = cameras.camera_id) 
     INNER JOIN 
    vehicle_vrn ON (pending_corrections.corrected_plate = vehicle_vrn.vrn500 
     OR pending_corrections.corrected_plate = vehicle_vrn.vrnno) 
     INNER JOIN 
    vehicle_ownership ON vehicle_vrn.fk_sysno = vehicle_ownership.fk_sysno 
WHERE 
    pending_corrections.seenDate >= '2015-01-01 00:00:00' 
     AND pending_corrections.seenDate <= '2015-01-31 23:59:59' 
ORDER BY pending_corrections.corrected_plate , pending_corrections.seenDate ASC; 

我怎樣才能達到同樣的效果,但沒有的一個OR加入?

+0

這種連接狀態看起來並不好:(pending_corrections.corrected_plate = vehicle_vrn.vrn500 OR pending_corrections.corrected_plate = vehicle_vrn.vrnno) - 在性能方面.. 。您是否嘗試使用EXISTS(...)子查詢而不是INNER JOINS,因爲您沒有從連接表中選擇任何數據? – MaxU

+2

'OR'ed連接條件是非常糟糕的(防止索引使用),常用的解決方法是使用'UNION'重寫。 – dnoeth

+0

@dnoeth你會碰巧建議如何使用UNION來制定它嗎? –

回答

1

重寫爲UNION很簡單,複製源代碼並刪除其中的每個OR條件:

SELECT 
    pending_corrections.corrected_plate , pending_corrections.seenDate 
FROM 
    (pending_corrections 
    INNER JOIN cameras ON pending_corrections.camerauid = cameras.camera_id) 
     INNER JOIN 
    vehicle_vrn ON (pending_corrections.corrected_plate = vehicle_vrn.vrn500) 
     INNER JOIN 
    vehicle_ownership ON vehicle_vrn.fk_sysno = vehicle_ownership.fk_sysno 
WHERE 
    pending_corrections.seenDate >= '2015-01-01 00:00:00' 
     AND pending_corrections.seenDate <= '2015-01-31 23:59:59' 

union 

SELECT 
    pending_corrections.corrected_plate , pending_corrections.seenDate 
FROM 
    (pending_corrections 
    INNER JOIN cameras ON pending_corrections.camerauid = cameras.camera_id) 
     INNER JOIN 
    vehicle_vrn ON pending_corrections.corrected_plate = vehicle_vrn.vrnno) 
     INNER JOIN 
    vehicle_ownership ON vehicle_vrn.fk_sysno = vehicle_ownership.fk_sysno 
WHERE 
    pending_corrections.seenDate >= '2015-01-01 00:00:00' 
     AND pending_corrections.seenDate <= '2015-01-31 23:59:59' 

ORDER BY 1,2; 

有沒有關於pending_corrections.seenDate的索引?

+0

是的,有一個索引。我已經試過這個聲明,但是仍然有兩個表掃描正在進行。 –

+0

下面是具有上述查詢的EXPLAIN語句。該查詢執行時間很長,因此似乎有錯誤。 http://imgur.com/AF6jlIG –

+0

@DotNET:返回多少行? 'pending_corrections'中有多少行以及WHERE條件有多少行? – dnoeth

0

你可以嘗試以下方法:

select 
    pending_corrections.corrected_plate , pending_corrections.seenDate 
from pending_corrections 
where pending_corrections.seenDate >= '2015-01-01 00:00:00' 
    and pending_corrections.seenDate <= '2015-01-31 23:59:59' 
    and exists(select 1 from cameras where pending_corrections.camerauid = cameras.camera_id) 
    and exists(select 1 from vehicle_ownership where vehicle_vrn.fk_sysno = vehicle_ownership.fk_sysno) 
    and exists(select 1 from vehicle_vrn 
      where pending_corrections.corrected_plate in (vehicle_vrn.vrnno, vehicle_vrn.vrn500)) 
order by 1,2; 

或者稱爲已經提到dnoeth:

select * from (
select 
    pending_corrections.corrected_plate , pending_corrections.seenDate 
from pending_corrections 
where pending_corrections.seenDate >= '2015-01-01 00:00:00' 
    and pending_corrections.seenDate <= '2015-01-31 23:59:59' 
    and exists(select 1 from cameras where pending_corrections.camerauid = cameras.camera_id) 
    and exists(select 1 from vehicle_ownership where vehicle_vrn.fk_sysno = vehicle_ownership.fk_sysno) 
    and exists(select 1 from vehicle_vrn where pending_corrections.corrected_plate = vehicle_vrn.vrnno) 
union 
select 
    pending_corrections.corrected_plate , pending_corrections.seenDate 
from pending_corrections 
where pending_corrections.seenDate >= '2015-01-01 00:00:00' 
    and pending_corrections.seenDate <= '2015-01-31 23:59:59' 
    and exists(select 1 from cameras where pending_corrections.camerauid = cameras.camera_id) 
    and exists(select 1 from vehicle_ownership where vehicle_vrn.fk_sysno = vehicle_ownership.fk_sysno) 
    and exists(select 1 from vehicle_vrn where pending_corrections.corrected_plate = vehicle_vrn.vrn500) 
) by 1,2; 

PS當然,我無法測試它自己,而無需數據,知道所有的指標

+0

我正在嘗試您提出的第一個查詢,並且收到以下錯誤:「未知列'vehicle_vrn.fk_sysno'在where子句中」。從我所看到的,這是在查詢中定義的,對嗎? –

+0

只是一個小小的評論,不推薦使用'order by ordinal position(從90年代的ANSI SQL標準中刪除)。使用列別名代替。 – jarlh

+0

fk_sysno - 需要實際加入到vrn中,而不僅僅在EXISTS中使用 –

0
 (SELECT pc.corrected_plate , pc.seenDate 
      FROM pending_corrections AS pc 
      INNER JOIN cameras AS c ON pc.camerauid = c.camera_id 
      INNER JOIN vehicle_vrn AS v ON pc.corrected_plate = v.vrn500 
      INNER JOIN vehicle_ownership AS vo ON v.fk_sysno = vo.fk_sysno 
      WHERE pc.seenDate >= '2015-01-01' 
       AND pc.seenDate < '2015-01-01' + INTERVAL 1 MONTH -- note improved pattern 
    ) 
    UNION ALL -- or use DISTINCT if you could have dups 
     (SELECT pc.corrected_plate , pc.seenDate 
      FROM pending_corrections AS pc 
      INNER JOIN cameras AS c ON pc.camerauid = c.camera_id 
      INNER JOIN vehicle_vrn AS v ON pc.corrected_plate = v.vrnno 
      INNER JOIN vehicle_ownership AS vo ON v.fk_sysno = vo.fk_sysno 
      WHERE pc.seenDate >= '2015-01-01' 
       AND pc.seenDate < '2015-01-01' + INTERVAL 1 MONTH 
    ) 
    ORDER BY corrected_plate , seenDate; 

您需要

pc: INDEX(seenDate) -- which you said you have 
c: INDEX(camera_id) -- unless you have PRIMARY KEY(camera_id) 
v: INDEX(vrn500) 
v: INDEX(vrnno) 
vo: INDEX(fk_sysno) -- sounds like it already exists