左连接查询的奇怪行为

时间:2012-05-14 15:37:12

标签: sql sql-server tsql

我有类似的查询:

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)

我得到一个奇怪的结果集,因为我期望从TableA返回每一行,但查询的日期部分搞砸了结果集只返回3行。我知道这一点,因为当我删除日期时间过滤器并留下其他两个过滤器时,我得到了正确的结果。

真的难倒这似乎是'意外行为'我无法从逻辑上解释为什么会发生这种情况。

4 个答案:

答案 0 :(得分: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 

答案 1 :(得分: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子句和NULL - WHERE子句中的扩展行很快就会失控。我可能会使用NOT EXISTS和/或简单UNION ALL重写查询,以便在尝试使用OUTER JOIN重写它之前获得正确的结果(可能是为了获得性能优势?)。

答案 2 :(得分: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现在可以被翻译成:select tableA中的每一行和tableB中的那些行,其中tableA中的col1等于来自tableB的col2,通过col3从tableB过滤结果,其中它是null或者来自tableB的col3,它是值在@StartDate和@EndDate之间。

希望它有所帮助。

最好的问候。

答案 3 :(得分:0)

我修复了这个问题,我只是将它们更改为派生表,以便在连接之前应用所有谓词。

感谢大家的帮助。