如何摆脱内连接中的OR条件

时间:2016-03-04 13:04:38

标签: mysql sql database performance

我有一个复杂的查询,需要总共4个表中的字段。我有一个内部连接语句,它有一个 SELECT pending_corrections.sightinguid AS 'pending_corrections_sightinguid', vehicle_ownership.id AS 'fk_vehicle_owner', @bill_id AS 'fk_bills', @nullValue AS 'fk_final_sightings_sightinguid', TRIM(pending_corrections.corrected_plate) AS 'vrn', pending_corrections.seenDate AS 'seen_date', cameras.in_out AS 'in_out', vehicle_vrn.fk_sysno AS 'fk_sysno', cameras.zone AS 'fk_zones', '0' AS 'auto_generated' 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.corrected_plate <> '' AND pending_corrections.corrected_plate IS NOT NULL AND pending_corrections.unable_to_correct <> '1' AND pending_corrections.seenDate >= @dateFrom AND pending_corrections.seenDate <= @dateTo AND (cameras.in_out = 1 OR cameras.in_out = 0) AND cameras.zone IN (SELECT zone_number FROM zones WHERE fk_site = @siteId) AND seenDate >= vehicle_vrn.vrn_start_date AND (seenDate <= vehicle_vrn.vrn_end_date OR vehicle_vrn.vrn_end_date IS NULL OR vehicle_vrn.vrn_end_date = '0001-01-01 00:00:00') AND seenDate >= vehicle_ownership.ownership_start_date AND (seenDate <= vehicle_ownership.ownership_end_date OR vehicle_ownership.ownership_end_date IS NULL OR vehicle_ownership.ownership_end_date = '0001-01-01 00:00:00') ORDER BY pending_corrections.corrected_plate , pending_corrections.seenDate ASC; 子句,这会大大减慢查询速度。

这是我的疑问:

OR

如何在其中一个连接中实现相同的效果但没有ORpending_corrections.corrected_plate子句的原因是vrn500值必须与vrnno表中的vehicle_vrn1) Trigger 'Wiki A' change 2) Verify content on 'Wiki B' 列匹配。

3 个答案:

答案 0 :(得分:1)

您可以使用IN表达式,例如:

,而不是使用带有OR的两个equals表达式
FROM
    (pending_corrections
    INNER JOIN cameras ON pending_corrections.camerauid = cameras.camera_id)
        INNER JOIN 
    vehicle_vrn ON pending_corrections.corrected_plate IN(vehicle_vrn.vrn500, vehicle_vrn.vrnno)
        INNER JOIN
    vehicle_ownership ON vehicle_vrn.fk_sysno = vehicle_ownership.fk_sysno

答案 1 :(得分:0)

执行计划是什么样的? SQL可能无法将此连接优化为散列或合并,因此只需执行表扫描。

有时使用OR,UNION运行良好。它的代码更多,但运行速度更快,因为SQL可以更好地优化它们。

; WITH PendingCorrectionsCte AS
(
    SELECT  pc.corrected_plate,
            pc.seen_date,
            c.in_out,
            pc.sightinguid AS 'pending_corrections_sightinguid',
    FROM    pending_corrections pc
            INNER JOIN cameras c ON pending_corrections.camerauid = cameras.camera_id
    WHERE   pc.seenDate BETWEEN '2015-01-01 00:00:00' AND '2015-01-31 23:59:59'
            AND NULLIF(LTRIM(pc.corrected_plate), '') IS NOT NULL
            AND pending_corrections.unable_to_correct <> '1'
            AND pending_corrections.seenDate >= @dateFrom
            AND pending_corrections.seenDate <= @dateTo
            AND c.in_out IN (0, 1)
            AND c.zone IN (SELECT zone_number FROM zones WHERE fk_site = @siteId)
),
VrnCte AS
(
    SELECT  pc.corrected_plate,
            pc.seen_date,
            vrn.fk_sysno,
            pc.in_out,
            pc.pending_corrections_sightinguid
    FROM    PendingCorrectionsCte pc
            INNER JOIN vehicle_vrn vrn ON pc.corrected_plate = vehicle_vrn.vrn500
            -- Could also do this inline in the where clause, but chaining isnull and nullif could get hard to read
            CROSS APPLY (SELECT NULLIF(vrn.vrn_end_date, '0001-01-01 00:00:00') AS value) vrn_end_date
    WHERE   pc.seenDate BETWEEN vrn.vrn_start_date AND ISNULL(vrn_end_date.value, seenDate)

    UNION

    SELECT  pc.corrected_plate,
            pc.seenDate,
            vrn.fk_sysno,
            pc.in_out,
            pc.pending_corrections_sightinguid
    FROM    pending_corrections pc
            INNER JOIN vehicle_vrn vrn ON pc.corrected_plate = vehicle_vrn.vrnno
            -- Could also do this inline in the where clause, but chaining isnull and nullif could get hard to read
            CROSS APPLY (SELECT NULLIF(vrn.vrn_end_date, '0001-01-01 00:00:00') AS value) vrn_end_date
    WHERE   pc.seenDate BETWEEN vrn.vrn_start_date AND ISNULL(vrn_end_date.value, seenDate)
)
SELECT  pending_corrections.sightinguid AS 'pending_corrections_sightinguid',
        vo.id AS 'fk_vehicle_owner',
        @bill_id AS 'fk_bills',
        @nullValue AS 'fk_final_sightings_sightinguid',
        TRIM(VrnCte.corrected_plate) AS 'vrn',
        VrnCte.seenDate AS 'seen_date',
        VrnCte.in_out AS 'in_out',
        VrnCte.fk_sysno AS 'fk_sysno',
        VrnCte.pending_corrections_sightinguid
        '0' AS 'auto_generated'
FROM    VrnCte 
        INNER JOIN vehicle_ownership vo ON VrnCte.fk_sysno = vo.fk_sysno
        CROSS APPLY (SELECT NULLIF(vo.ownership_end_date, '0001-01-01 00:00:00') AS value) ownership_end_date
WHERE   VrnCte.seenDate BETWEEN vehicle_ownership.ownership_start_date AND ISNULL(ownership_end_date.value, seenDate)
ORDER BY 
        VrnCte.corrected_plate, 
        VrnCte.seenDate ASC

答案 2 :(得分:0)

您可以使用OR替换第一个IN(),如Scott提到的那样

pending_corrections.corrected_plate IN (vehicle_vrn.vrn500, vehicle_vrn.vrnno)

如果您可以UPDATE vrn_end_dateownership_end_date'0001-01-01 00:00:00'NULL,其他OR条件可以简化为< / p>

AND (seenDate <= IFNULL(vehicle_vrn.vrn_end_date, seenDate))