SQL Where子句排除了它应该超过它

时间:2014-03-24 18:00:57

标签: sql tsql where-clause

我有两个带连接和where子句的表。 2个表的示例内容:

Id         FieldA                   Id       FieldB
 1           100                     1        Yellow
 2           100                     2        Green
 3           200                     3        Green
 4           200                     4        Blue
 5           300                     5        Yellow
 6           300                     6        Orange

我试图返回除了fieldA = 200 AND fieldB = Green之外的所有内容。所以它仍然应该返回具有fieldA = 100和FieldB = Green的第2行。但是,这是我的查询,它不起作用。它排除了200和绿色的所有行:

select t1.FieldA, t2.FieldB
FROM test1 t1 
JOIN test2 t2 ON t1.Id = t2.Id
WHERE (t1.FieldA <> 200 AND t2.FieldB <> 'Green')

我看到它的方式,在运行此查询后,排除的唯一行应该是第3行,因为它有fielda = 200和fieldb = green,但它只返回row1,第5行和&amp; ROW6。在我看来,如果我使用OR,它应该只会这样做。

让我知道我哪里出错了,这里有一些DDL,所以你可以玩它:

create table dbo.test1
(
    Id int not null,
    FieldA int
)

create table dbo.test2
(
    Id int not null,
    FieldB varchar(10)
)

INSERT INTO test1 (Id, FieldA)
VALUES 
    (1,100),
    (2,100),
    (3,200),
    (4,200),
    (5,300),
    (6,300)

INSERT INTO test2 (Id, FieldB)
VALUES 
    (1,'Yellow'),
    (2,'Green'),
    (3,'Green'),
    (4,'Blue'),
    (5,'Yellow'),
    (6,'Orange')

4 个答案:

答案 0 :(得分:4)

每个条件都是针对整个行集独立评估的。要合并它们,请翻转操作符并取消组合,如下所示:

select t1.FieldA, t2.FieldB
FROM test1 t1 
JOIN test2 t2 ON t1.Id = t2.Id
WHERE not (t1.FieldA = 200 AND t2.FieldB = 'Green')

您的原始查询基本上是说,首先消除FieldA不是200的所有行,然后从剩余的行中消除所有FieldB不是“绿色”的行。

当您希望两个条件都适用于给定行时,首先要选择要排除的条件,这就是您从<>切换到=,然后生成{{1 }}子句通过应用WHERE运算符排除整个事物。

编辑重新评论

我认为关于原始查询返回的结果的混淆以及括号中的条件被“评估为一个”的想法可能源于逻辑否定不是分配的事实,即NOT的否定是不是A && B,而是~A && ~B

您描述所需结果的第一句话非常接近查询的正确t-sql。你说“我试图返回除了fieldA = 200和fieldB = Green之外的所有东西。”句子的最后一部分是你的where子句,即

~(A && B)

将“not”替换为“except”

except where fieldA = 200 AND fieldB = Green

并将其清理为有效的t-sql语法

not where fieldA = 200 AND fieldB = Green
-- or, to make the grouping explicit
not (where fieldA = 200 AND fieldB = Green)

相比之下,相当于where not (fieldA = 200 AND fieldB = Green) 的英语可能是:返回field1除了200以外的所有内容,而field1是绿色的。在这种情况下,匹配200或绿色就足以排除该行。

要查看错误地排除第2行和第4行的原因,请考虑原始where子句的真值表:

WHERE (t1.FieldA <> 200 AND t2.FieldB <> 'Green')

换句话说,第2行被排除在 Field1 <> 200 T F ----------------- T | T | F | | | row 4 | Field2 <> 'Green' ----------------- F | F | F | | row 2 | | ----------------- 之后,条件Field2 = 'Green'评估为Field2 <> 'Green',因此FALSE是什么并不重要,因为{ {1}}和任何其他值始终为Field1

答案 1 :(得分:2)

当你在Id上进行连接时,它会创建一个临时表,如下所示

Id  FieldA  FieldB
1   100  Yellow
2   100  Green
3   200  Green
4   200  Blue
5   300  Yellow
6   300  Orange

当你说FieldA&lt;&gt;在图200中,行3和4被排除,剩余的行将是1,2,5,6。 现在,当你说FieldB&lt;&gt;绿色,然后第2行被排除,导致行1,5,6。

注意:条件&#39;其中&#39;条款不会根据您指定的顺序应用。相反,它在运行时应用于sql执行计划,并且指定where条件的顺序不会对结果产生任何影响。

要获得结果,请使用以下条件

select t1.FieldA, t2.FieldB
FROM test1 t1 
JOIN test2 t2 ON t1.Id = t2.Id
WHERE NOT (t1.FieldA = 200 AND t2.FieldB = 'Green')

答案 2 :(得分:1)

select t1.FieldA, t2.FieldB
FROM test1 t1 
JOIN test2 t2 ON t1.Id = t2.Id
WHERE NOT (t1.FieldA = 200 AND t2.FieldB = 'Green')

答案 3 :(得分:0)

咧嘴笑,你可以尝试一下吗?

select t1.FieldA, t2.FieldB
  FROM test1 t1 
  JOIN test2 t2 
    ON t1.Id = t2.Id
   AND t1.FieldA <> 200 
   AND t2.FieldB <> 'Green'