【问题标题】:SQL Where clause excluding more than it shouldSQL Where 子句排除了超出应有的范围
【发布时间】:2014-05-02 06:21:45
【问题描述】:

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

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,但它只返回第 1 行、第 5 行和第 6 行。在我看来,只有在我使用 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')

【问题讨论】:

    标签: sql tsql where-clause


    【解决方案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')
    

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

    当您希望两个条件都适用于给定行时,您首先选择要排除的条件,这就是您从&lt;&gt; 切换到= 的原因,然后让您的WHERE 子句排除整个通过应用NOT 运算符来实现。

    编辑回复:评论

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

    您描述您想要的结果的第一句话非常接近查询的正确 t-sql。您说“我正在尝试返回除 fieldA = 200 AND fieldB = Green 之外的所有内容。”句子的最后一部分是你的 where 子句,即

    except where fieldA = 200 AND fieldB = Green
    

    用“不”代替“除”

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

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

    where not (fieldA = 200 AND fieldB = Green)
    

    相比之下,相当于WHERE (t1.FieldA &lt;&gt; 200 AND t2.FieldB &lt;&gt; 'Green') 的英文可能是:返回field1 不是200 和field1 不是绿色的所有内容。在这种情况下,匹配 200 或绿色就足以排除该行。

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

                           Field1 <> 200
    
                             T       F
                         -----------------
                       T |   T   |   F   |
                         |       | row 4 |
    Field2 <> 'Green'    -----------------
                       F |   F   |   F   |
                         | row 2 |       |
                         -----------------
    

    换句话说,第 2 行被排除,因为Field2 = 'Green',使条件Field2 &lt;&gt; 'Green' 评估为FALSE,所以Field1 是什么并不重要,因为FALSE 和任何其他值总是FALSE.

    【讨论】:

    • 听起来不错,谢谢。但是我认为因为这两个子句都在一个共享的()中,所以它会被评估为一个。这不是真的吗?
    • @teahou 括号中的条件确实被“评估为一个”,它们只是没有达到您的预期。它们对于您的原始查询也是多余的,但是当我们添加 NOT 运算符时它们确实会有所不同。有关详细信息,请参阅我的更新。
    • 很有意义,谢谢。太糟糕了,我只能投票一次:)
    【解决方案2】:

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

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

    当您说 FieldA 200 时,将排除第 3 行和第 4 行,其余行将是 1、2、5、6。 现在,当您说 FieldB Green 时,第 2 行被排除在外,导致第 1、5、6 行。

    注意: 'where' 子句中的条件不会根据您指定的顺序应用。而是应用在运行时的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')
    

    【讨论】:

      【解决方案3】:
      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')
      

      【讨论】:

        【解决方案4】:

        你可以试试这个吗?

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

        【讨论】:

          猜你喜欢
          • 2011-12-28
          • 2019-07-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-10-21
          • 1970-01-01
          相关资源
          最近更新 更多