【问题标题】:Problems with INNER JOIN and LEFT/RIGHT OUTER JOININNER JOIN 和 LEFT/RIGHT OUTER JOIN 的问题
【发布时间】:2013-05-02 19:33:18
【问题描述】:

我有三张桌子:

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


  • 客户
    • CustomerId,int PK
    • CompanyId,int FK to Company,NULL 不允许


  • 公司
    • 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

这个查询有我期望的内连接和外连接的数量,但问题是我很难阅读,因为我将我的查询作为 orders 的查询考虑到订单在哪里是选择的“根”而不是公司。 RIGHT OUTER JOIN 的用法我也比较陌生。

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

问题:

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

【问题讨论】:

  • 我不是说你错了,但订单怎么可能没有客户。订单肯定是产品和客户的结合
  • @DavidB:不是真正的模特。想想“匿名”订单,生产公司有内部订单要在没有客户参考的情况下生产库存......或类似的东西。
  • 这里的一个解决方案是拥有一个可以匹配这些的“匿名”客户记录,而不是根本没有客户。您可能会发现这种方法对许多其他报告也有帮助。

标签: sql sql-server tsql join


【解决方案1】:

在语义上,连接按照它们在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

【讨论】:

  • 谢谢你,很好的解释!与括号解决方案相比,子查询是否对性能有任何影响,或者查询计划是否仍然相同?
  • @Slauma 。 . .在 SQL Server 中,我认为没有影响。引擎优化整个查询。在其他数据库中,它可能会有所不同(例如,MySQL 会实例化子查询,因此该数据库中的查询计划会有所不同)。
【解决方案2】:
  1. 查询 1:因为您在客户上有一个 INNER JOIN,所以 LEFT JOIN 实际上是一个 INNER JOIN
  2. 查询 2 是正确的,因为无论数据质量/条件如何,您都希望查看所有订单。
  3. 一般来说,我喜欢避免使用RIGHT JOINs,因为它会让一些开发人员感到困惑,因此可读性较差。您通常可以通过有效使用LEFT JOINs 来编写查询以执行相同的操作。
  4. 查询 2 是我对这样简单的事情的建议。
  5. 一个一般规则...一旦您在查询中引入OUTER JOIN,随后的JOINs 也应该是OUTER JOINs。否则,您可以排除您不想要的行。

【讨论】:

  • 我已经开始写一个答案,但是在这个答案突然出现之后我就停止了,因为这正是我要在所有问题上说的。也许 1. 可以使用更多解释,但一般规则应该是:首先使用 INNER JOIN(过滤行),然后使用 LEFT JOIN(获取更多数据)。
【解决方案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

【讨论】:

  • 确实括号好像不是必须的,我只是测试了一下。谢谢!
【解决方案4】:

查询 1 在 Company 上有 INNER JOIN,这意味着订单需要有有效的 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

【讨论】:

    【解决方案5】:

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

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

    3) 第三个查询很好,但很难看。你加入 company 和 customer 表,然后说无论结果集中有什么,我都想要来自 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) 用谷歌搜索更容易回答这个问题。我相信我可以在几分钟内写出更全面的信息。

    【讨论】:

      猜你喜欢
      • 2019-09-03
      • 2013-03-10
      • 2014-01-07
      • 2015-01-10
      • 1970-01-01
      • 2011-03-12
      • 2012-02-27
      • 2016-10-31
      相关资源
      最近更新 更多