如何处理多个连接

时间:2016-03-04 10:03:00

标签: mysql sql database performance relational-database

我有一个复杂的查询,需要总共4个表中的字段。内部联接导致查询花费的时间比应该的长。我运行了一个EXPLAIN语句,其可视化结果如下:

EXPLAIN Statement output

这是我的疑问:

struct Size
{
    const unsigned int width;
    const unsigned int height;

    static const Size big;
    static const Size small;

private:

    Size( ) = default;

    // something to declare the aggregare initialization as private
};

const Size Size::big = { 480, 240 };
const Size Size::small = { 210, 170 };

如何在其中一个连接中实现相同的效果但没有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;

3 个答案:

答案 0 :(得分: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上有索引吗?

答案 1 :(得分: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当然,如果没有数据并知道所有索引,我自己无法测试

答案 2 :(得分: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