为什么此语句仅适用于WHERE?

时间:2014-10-30 18:18:55

标签: sql

我有一个Customers表和一个订单表。并非所有客户都下订单,因此并非所有客户ID都在订单表中。我希望我的结果只显示来自customer表的未下订单的值,因此orderID列应显示为null。以下代码有效:

SELECT c.CustomerID, c.CustomerName, o.OrderID
  FROM Customers c
    LEFT OUTER JOIN Orders o
ON c.CustomerID = o.CustomerID WHERE o.OrderID IS NULL

但我最初尝试的那个不是:

SELECT c.CustomerID, c.CustomerName, o.OrderID
  FROM Customers c
    LEFT OUTER JOIN Orders o
ON c.CustomerID = o.CustomerID AND o.OrderID IS NULL

这个显示了customers表上的所有值,但是所有这些值的orderID都为null

我不认为我真的理解这种差异,因为我觉得他们两个都返回相同的东西是有意义的。

8 个答案:

答案 0 :(得分:6)

重要的是要明确两个查询非常不同。第一个查询是:

SELECT c.CustomerID, c.CustomerName, o.OrderID
FROM Customers c LEFT OUTER JOIN
     Orders o
     ON c.CustomerID = o.CustomerID
WHERE o.OrderID IS NULL;

它返回没有相应订单的所有客户(或o.OrderId为空)。这样做是因为left outer join保留了第一个表中的所有行。如果没有匹配项,则第二个表中的所有列都为NULLwhere子句将选择这些列。

第二个问题:

SELECT c.CustomerID, c.CustomerName, o.OrderID
FROM Customers c LEFT OUTER JOIN
     Orders o
     ON c.CustomerID = o.CustomerID AND
        o.OrderID IS NULL;

查找所有客户的所有行,并且如果存在任何此类记录,还会获取OrderId为空的订单信息。没有Customers的过滤,因为left outer join保证第一个表中的所有行都在结果集中。

如果名为OrderId的字段带有NULL值,我会感到惊讶。但是,每个查询都是有效的SQL,每个查询都有用。但是,只有其中一个符合您的意图。

答案 1 :(得分:3)

使用LEFT OUTER JOIN,您可以从Customers表中获取所有记录。因此,您的AND o.OrderID IS NULL条件仅过滤来自Orders表的记录,而不过滤来自Customers的记录。但你需要的是过滤Customers表,由于JOIN的类型,它不起作用。

同时,无论JOIN的类型如何,都使用WHERE条件应用于整个记录集。尝试将LEFT OUTER JOIN替换为INNER JOIN,两个SELECT都会得到相同的结果。

在第二个SELECT中,您获得o.OrderID的NULL值,因为您指定它在条件AND o.OrderID IS NULL中应为NULL。 Orders表中不存在此类记录,因此NULL值表示没有记录符合条件ON c.CustomerID = o.CustomerID AND o.OrderID IS NULL

答案 2 :(得分:2)

这是外连接的性质(在这种情况下是左连接)。左连接采用主表(Customers),按标准匹配连接表(Orders)。对于没有匹配项的Customers中的每一行,与内部联接不同,它不会删除该行。相反,它会添加Orders中的所有字段,但会在其中添加null。

看看这个例子:

 Table A           Table B
┌──────┬──────┐ ┌──────┬──────┐
│field1│field2│ │field3│field4│
├──────┼──────┤ ├──────┼──────┤
│A     │1     │ │1     │One   │
│B     │2     │ │3     │Three │
│C     │3     │ └──────┴──────┘
└──────┴──────┘

表的内连接(在field2和field3之间)是:

┌──────┬──────┬──────┬──────┐
│field1│field2│field3│field4│
├──────┼──────┤──────┼──────┤
│A     │1     │1     │One   │
│C     │3     │3     │Three │
└──────┴──────┴──────┴──────┘

但是表的外连接必须为你提供每条记录,如果没有匹配,则改为输入空值。

┌──────┬──────┬──────┬──────┐
│field1│field2│field3│field4│
├──────┼──────┤──────┼──────┤
│A     │1     │1     │One   │
│B     │2     │NULL  │NULL  │⬅︎ No match
│C     │3     │3     │Three │
└──────┴──────┴──────┴──────┘

如果table2中根本没有匹配,会发生什么?例如,如果在ON子句中添加了一个不可能的条件?然后结果中的所有记录看起来都像“不匹配”

┌──────┬──────┬──────┬──────┐
│field1│field2│field3│field4│
├──────┼──────┤──────┼──────┤
│A     │1     │NULL  │NULL  │⬅︎ No match (because of impossible condition)
│B     │2     │NULL  │NULL  │⬅︎ No match (because of impossible condition)
│C     │3     │NULL  │NULL  │⬅︎ No match (because of impossible condition)
└──────┴──────┴──────┴──────┘

因此,如果没有匹配则无关紧要,因为Table2中没有给定ID的记录,或者因为添加了不可能的条件而没有匹配。外连接的结果是应该来自Table2的字段将被替换为空值。因为这是定义外连接的方式。


现在到现实世界的表格:

在OrderID为null的订单中,您实际上没有任何记录(除非您设计得非常糟糕)。因此,如果您将该条件放在ON子句中,它将找不到符合您条件的记录。

在这种情况下,因为这是一个外部(左)联接,您将获得所有原始客户记录,并且因为没有匹配,所以每个都具有Orders中的字段全部为空。

如果您将条件放在WHERE中,那么您实际上正在充分利用左连接的这种行为。您将每个客户与其订单进行匹配。如果有匹配 - 很好,你得到了实际的订单ID。但是如果没有匹配 - 你正在寻找的匹配 - 它会添加一个空订单ID。

然后where子句告诉它只给你发生的记录。也就是说,订单中没有匹配订单的记录。

答案 3 :(得分:1)

根本不是SQL的设计方式。来自http://dev.mysql.com/doc/refman/5.0/en/join.html

  

与ON一起使用的conditional_expr是的任何条件表达式   可以在WHERE子句中使用的表单。一般来说,你应该使用   指定如何连接表的条件的ON子句,以及   WHERE子句,用于限制结果集中所需的行。

答案 4 :(得分:1)

第一个是POST连接表。第二部分是加入的一部分。

所以要用英语。

第一:

 Get CustomerID, Name, and OrderID from
 Customers Link to Orders where CustomerID matches
 Show where link failed

第二

Get CustomerID, Name, and OrderID from
Customers Link to Orders where CustomerID matches and Order ID is null

所有链接都在第二次失败,没有任何内容可以过滤您的结果。

答案 5 :(得分:1)

对于第二个查询,每个行的连接条件为false。如果Order表具有CustomerId,那么它也将具有OrderId。在排除每一行之后," left"连接的一部分只是从左表(Customer)中返回所有排除的行。在这种情况下,它会带回所有这些。

对于第一个查询,它只对您想要的行显示为false,因此左连接只会恢复这些行。然后where条件可以取出你不想要的行。

答案 6 :(得分:1)

创建表## Customer(Id nvarchar(5)) 创建表## Order(Order_No int,Id nvarchar(5))

插入##客户价值('A'),('B'),('C')

插入##订单值(1,'A'),(2,'B'),(3,'B')

从## customer c中选择c。* left join ## Order o 在c.Id = o.id,其中o.Order_No为空

从## customer c中选择c.id,o.order_no left join ## Order o 在c.Id = o.id和o.Order_No为空

从## customer c中选择c.id,o.order_no left join ## Order o 在c.Id = o.id和o.Order_No不为空

亲自尝试你会发现差异,where子句给出了一个过滤器

答案 7 :(得分:1)

LEFT OUTER JOIN保留左(第一)表中不匹配的行,将它们与右(第二)表形状的NULL行连接起来。 让我们举一个具体的例子。 比如,Customers表有{c1,"n1"}, {c2,"n2"} 和订单{c1, o1} 第一例: 左外连接将导致:

{c1,"n1", o1}, {c2,"n2", null}

现在,您知道哪里和" AND"的区别。在哪里找到你有null orderid的地方,对于AND,没有这样的情况,其中客户id匹配且订单id为null