INNER JOIN和LEFT / RIGHT OUTER JOIN的问题

时间:2013-05-08 14:08:59

标签: sql sql-server tsql join

我有三张桌子:

  • 订单
    • OrderId,int PK
    • CustomerId,int FK to Customer,允许NULL


  • 客户
    • CustomerId,int PK
    • CompanyId,int FK to Company,NULL not allowed


  • 公司
    • CompanyId,int PK
    • 姓名,nvarchar(50)

我想选择所有订单,无论他们是否有客户,如果他们有客户,那么也是客户的公司名称。

如果我使用此查询...

SELECT Orders.OrderId, Customers.CustomerId, Companies.Name
FROM   Orders
       LEFT OUTER JOIN Customers
           ON Orders.CustomerId = Customers.CustomerId
       INNER JOIN Companies
           OM Customers.CompanyId = Companies.CompanyId

...它只返回拥有客户的订单。如果我将INNER JOIN替换为LEFT OUTER JOIN ...

SELECT Orders.OrderId, Customers.CustomerId, Companies.Name
FROM   Orders
       LEFT OUTER JOIN Customers
           ON Orders.CustomerId = Customers.CustomerId
       LEFT OUTER JOIN Companies
           OM Customers.CompanyId = Companies.CompanyId

...它有效,但我不明白为什么这是必要的,因为CustomersCompanies之间的关系是必需的:客户必须拥有公司。

另一种可行的方法似乎是:

SELECT Orders.OrderId, Customers.CustomerId, Companies.Name
FROM   Companies
       INNER JOIN Customers
           ON Companies.CompanyId = Customers.CompanyId
       RIGHT OUTER JOIN Orders
           OM Customers.CustomerId Orders.CustomerId

此查询具有我期望的内部和外部联接的数量,但问题是它很难为我阅读,因为我将查询作为订单的查询记住了订单是选择的“根”,而不是公司。我也不熟悉RIGHT OUTER JOIN的用法。

最后一个查询是设计器为SQL Server Reporting Services报告生成的查询的一小部分。我试图在没有设计器表面的情况下手动编写查询,因为它非常过度拥挤,并且在将来需要进行多次更改和更多更改之后,我遇到了维护查询的问题。所以,我想以某种方式给查询一个可读的结构。

问题:

  1. 为什么不像我预期的那样查询1个工作?
  2. 查询2是正确的解决方案虽然(或因为?)它使用了两个LEFT OTHER JOINS?
  3. 查询3是正确的解决方案吗?
  4. 有更好的方法来编写查询吗?
  5. 是否有一些通用的经验法则和实践如何以良好的可读方式编写具有大量外部和内部联接的查询?

5 个答案:

答案 0 :(得分:12)

从语义上讲,连接按照它们在from子句中出现的顺序进行处理。 (由于SQL优化,它们可能不会按此顺序实际执行,但排序对于定义结果集很重要。)

所以,当你这样做时:

from orders left outer join customers inner join companies

(我遗漏了on条款,这些条款会为此目的分散注意力。)

SQL被解释为:

from (orders left outer join customers) inner join companies

您正在执行inner join,因此值必须出现在两侧。在您的情况下,这会撤消left outer join

的影响

你想:

from orders left outer join (customers inner join companies)

以下是一些解决方案。

我首选的解决方案是对所有联接使用left outer join。实际上,为了可读性和可维护性,我编写的几乎每个查询都只是left outer join[inner] join连接表。如果你能以一致的形式编写查询,那么必须解析查询以理解连接的语义似乎是不必要的努力。

另一种解决方案是使用括号:

from orders left outer join (customers inner join companies)

另一个解决方案是子查询:

from orders left outer join (select . . . from customers inner join companies) cc

答案 1 :(得分:4)

  1. 问题1:由于您的客户有INNER JOIN,因此LEFT JOIN实际上是INNER JOIN
  2. 查询2是正确的,因为无论数据质量/条件如何,您都希望查看所有订单。
  3. 我喜欢避免RIGHT JOIN一般,因为它让一些开发人员感到困惑,因此不太可读。您通常可以通过有效使用LEFT JOIN来以同样的方式编写查询。
  4. 查询2是我对这样简单的建议。
  5. 一般规则...在您的查询中引入OUTER JOIN后,后面的JOIN也应为OUTER JOIN。否则,您 MAY 会排除您不想要的行。

答案 2 :(得分:3)

您可以像这样编写嵌套连接,以便对客户和公司的组合结果执行左连接,而不是对订单和客户的组合结果执行内连接。我基本上只是将内连接移动到左外连接的ON子句之前。其他人建议使用括号来获得此结果,如果内存服务,两种语法都将导致相同的执行。

SELECT Orders.OrderId, Customers.CustomerId, Companies.Name
FROM   Orders
LEFT OUTER JOIN Customers
    INNER JOIN Companies
        ON Customers.CompanyId = Companies.CompanyId
    ON Orders.CustomerId = Customers.CustomerId

答案 3 :(得分:2)

查询1在公司有INNER JOIN,这意味着订单需要有vaild Customer(CompanyID)    如果你想使用INNER JOIN,它可以是这样的

SELECT Orders.OrderId, a.CustomerId, a.Name
FROM   Orders
LEFT JOIN (
  SELECT Customers.CustomerId, Companies.Name
  FROM Customers
  INNER JOIN Companies
           OM Customers.CompanyId = Companies.CompanyId
) a 
  ON  Orders.CustomerId = a.CustomerId

答案 4 :(得分:2)

1)它不起作用,因为当您INNER JOINCompanies时,您需要在整个联接中存在,但由于Customer不存在订单无法将Companies记录关联回订单,因此不会返回。

2)我想如果你能获得没有相关公司的Customer记录,你可以使用第二个查询,但如果这些表之间的关系是1比1,那就应该没问题。

3)第三个查询很好,但很难看。您加入公司和客户表,然后说不管结果集中有什么,我想要Orders的所有内容。

4)我可能会加入子查询中的客户和公司,并将其重新加入订单。

查询:

SELECT  Orders.OrderId, 
        Subquery.CustomerId, 
        Subquery.Name
FROM    Orders
LEFT    OUTER JOIN 
       (Select  Customers.CustomerID,
                Companies.Name
        From    Customers
        INNER   JOIN Companies
                ON Customers.CompanyId = Companies.CompanyId) Subquery
        On Orders.CustomerID = Subquery.CustomerID

5)谷歌搜索更容易回答这个问题。我确信可以在几分钟内写出更全面的信息。