How to handle multiple joins

℡╲_俬逩灬. 提交于 2019-12-02 10:16:05

问题


I have a complex query which requires fields from a total of 4 tables. The inner joins are causing the query to take much longer than it should. I have run an EXPLAIN statement, whose visual result is attached below:

This is my query:

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;

How can I achieve the same effect but without the OR in one of the joins?


回答1:


Rewriting as a UNION is simple, copy the source and remove one of the ORed conditions in each:

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;

Is there an index on pending_corrections.seenDate?




回答2:


you may try the following:

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;

or alternatively as dnoeth already mentioned:

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 of course i couldn't test it myself without having the data and knowing all your indexes




回答3:


      ( 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; 

You will need

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


来源:https://stackoverflow.com/questions/35793086/how-to-handle-multiple-joins

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!