2016-03-04 59 views
1

我有一个复杂的查询需要从总共4个表中的字段。我有一个内部联接语句,它有一个OR子句,这大大减缓了查询的速度。如何摆脱内部连接OR条件

这是我的查询:

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; 

我怎样才能实现的加入一个相同的效果,但没有OROR子句的原因是因为pending_corrections.corrected_plate值必须与vehicle_vrn表中的vrn500vrnno列匹配。

+1

这是您的最后一个问题的副本,然后您在发布答案后更改它。来吧。 – Paparazzi

+0

不是@Frisbee - 这个问题更具体,因为它只处理内部连接的OR部分,而不是整个查询本身。 –

+0

从复制品“我怎样才能达到相同的效果,但在一个连接中没有OR?” – Paparazzi

回答

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 
+0

我编辑了问题以包含完整查询。 –

+0

好的,编辑后显示一种可能的方法,通过链接CTE来使UNION的pending_corrections过滤器更容易维护。 –

+0

“WITH”的第一部分是什么? –

1

除了使用二等于与或表达式,你可以使用IN的表达,如:

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 
+0

感谢您的建议,但是当我运行EXPLAIN语句时,似乎无论如何都会导致两次全表扫描。 –

0

可以使用IN()取代第一OR,斯科特提到

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

如果您可以UPDATEvrn_end_dateownership_end_date列从'0001-01-01 00:00:00'NULL,其他OR条件可以简化为

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

感谢您的建议,但是当我运行EXPLAIN语句时,似乎无论如何都会导致两次全表扫描。 –

+0

表现可能更差,但它确实摆脱了“或”;) –

+0

正确但不幸的是,表现似乎相同或更差。我应该澄清,但想摆脱OR的原因是为了表现:) –