2012-05-14 32 views
-2

我有一個類似的查詢:古怪的行爲聯接查詢

SELECT * 
FROM TableA a 
LEFT JOIN TABLEB b 
ON a.ColA = b.ColA 
WHERE b.Col1 IS NULL 
OR (b.Col1 = A AND b.Col2 = B AND b.Col3 BETWEEN @StartDate AND @EndDate) 

我得到一個奇怪的結果集,因爲即時通訊期待每一行從表A返回,但查詢的日期部分會被搞砸了結果集只返回3行。我知道這一點,因爲當我刪除日期時間過濾器,並留下其他兩個過濾器,然後我得到正確的結果。

真的難倒這似乎是'意外的行爲'我不能從邏輯上解釋爲什麼會發生這種情況。

+11

你不應該得到任何結果集。你應該得到一個錯誤,因爲語法是無效的(JOIN沒有ON)。請複製並粘貼運行的實際查詢。 –

+1

另外,請提供樣本數據和期望的輸出。 – RedFilter

+0

根據具體的數據庫,這可能會執行,但會導致笛卡爾聯接。 – kirbs

回答

3

儘管你的語法錯誤....

你應該執行正確的加入,因爲在WHERE子句中的所有邏輯上表B.

SELECT * 
FROM TABLEB b 
RIGHT JOIN TABLEA a on a.SomeId = b.SomeId 
WHERE b.Col1 IS NULL OR (b.Col1 = A AND b.Col2 = B AND b.Col3 BETWEEN @StartDate AND @EndDate) 

然而,這可能是你真的希望,而不是...

SELECT * 
FROM TableA a 
LEFT JOIN TABLEB b on b.SomeId = a.SomeId and b.Col1 = A AND b.Col2 = B AND b.Col3 BETWEEN @StartDate AND @EndDate 
WHERE b.Col1 IS NULL 
+2

FWIW我不同意將所有過濾條件放在您的左連接示例的ON子句中。有很好的理由從過濾標準中分離出連接標準(如果我們能猜到它是什麼)... –

+0

@AaronBertrand - 將邏輯放在ON子句中有時是必需的,並且會提供不同的結果,然後將完全相同的邏輯放入WHERE子句。 –

+1

這也不起作用。我知道如何編寫一個外部聯接,因爲我在辦公室裏,所以我只是匆匆打字。 就像我說的,這是'意想不到的行爲',我的代碼'正確',只是日期時間部分搞砸了。你們在這裏專注於錯誤的細節,但我很感激迄今爲止所有的幫助。 –

0

你的SQL是這樣子寫的更好:

SELECT * 
FROM TableA a 
LEFT JOIN TABLEB b ON a.col1 = b.col2 
WHERE (b.col3 IS NULL) or (b.Col3 BETWEEN @StartDate AND @EndDate) 

b.Col1 = A AND b.Col2 = B應該在LEFT JOIN塊中,因爲這是左連接語句的正確語法。

因此,上面的SQL可能會被翻譯爲:從tableA中選擇每一行,並從tableB中選擇tableA中的col1與tableB中的col2相等的col2,通過col3從tableB中過濾結果,其中它是null或由tableB中的col3它的價值在@StartDate和@EndDate之間。

希望它有幫助。

此致敬禮。

+1

該查詢仍然會過濾掉b.Col3爲空的結果。您需要考慮由於左連接而導致b.Col3爲空。 –

+0

Ops,正確的,已經逃脫了我......我編輯了我的答案......希望你現在投票... –

+0

「沒有必要,因爲左連接已經對此做了計算」 - 這是不正確的。 –

2

這是否接近重現您所看到的問題?

DECLARE @StartDate datetime; 
DECLARE @EndDate datetime; 

-- Date range is the year 2000 
SET @StartDate = '2000-01-01'; 
SET @EndDate = '2000-12-31'; 

DECLARE @TableA TABLE 
(
    col1 int NULL, 
    col2 int NULL 
); 

DECLARE @TableB TABLE 
(
    col1 int NULL, 
    col2 int NULL, 
    col3 datetime NULL 
); 

和:

INSERT @TableA 
    (col1, col2) 
VALUES 
    (1, 1); 

-- Row joins but date is not in year 2000 
INSERT @TableB 
    (col1, col2, col3) 
VALUES 
    (1, 1, '2001-07-01'); 

-- Test query (no rows) 
SELECT 
    a.*, 
    b.* 
FROM @TableA AS a 
LEFT JOIN @TableB AS b 
    ON b.col1 = a.col1 
WHERE 
    b.col1 IS NULL 
OR 
(
    b.col1 = a.col1 
    AND b.col2 = a.col2 
    AND b.col3 BETWEEN @StartDate AND @EndDate 
); 

這是很容易爲外部的邏輯連接,ON條款和WHERE子句中NULL -Extended行迅速失控。在使用OUTER JOIN嘗試重寫它(推測是爲了獲得性能好處?)之前,我可能會重寫查詢,使用NOT EXISTS和/或簡單的UNION ALL以獲得正確的結果。

+0

第二個包含所有重要組件,儘管我原來的查詢有更多的過濾器。其他過濾器是一個非因素,雖然當我把日期一出來它工作正常。無論如何,就像我提到的,我只是將其表示爲派生表,而且它工作得很好。 –

0

我解決了這個問題,我只是將它們更改爲派生表,所以所有謂詞都在連接之前應用。

感謝大家的幫助。

+0

如果您分享解決方案,會比其他類似問題的解決方案能夠快速解決問題。 –