2016-03-04 20 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的原因是爲了表現:) –