如何处理多个连接
How to handle multiple joins
我有一个复杂的查询,它需要总共 4 个表中的字段。内部联接导致查询花费的时间比应有的时间长得多。我有 运行 一个 EXPLAIN 语句,其可视化结果附在下面:
这是我的查询:
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
的情况下实现相同的效果?
您可以尝试以下方法:
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 当然,如果没有数据和知道你所有的索引,我无法自己测试它
重写为 UNION
很简单,复制源代码并删除每个 OR
ed 条件之一:
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
上有索引吗?
( 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
我有一个复杂的查询,它需要总共 4 个表中的字段。内部联接导致查询花费的时间比应有的时间长得多。我有 运行 一个 EXPLAIN 语句,其可视化结果附在下面:
这是我的查询:
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
的情况下实现相同的效果?
您可以尝试以下方法:
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 当然,如果没有数据和知道你所有的索引,我无法自己测试它
重写为 UNION
很简单,复制源代码并删除每个 OR
ed 条件之一:
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
上有索引吗?
( 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