7

我有三张桌子:

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


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


  • 公司
    • 公司 ID,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 JOINLEFT 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. 是否有一些通用的经验法则和实践如何以一种可读的方式编写具有大量外部和内部联接的查询?
4

5 回答 5

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
于 2013-05-08T14:21:00.787 回答
4
  1. 查询 1:因为您有一个INNER JOINon 客户,所以LEFT JOIN实际上是一个INNER JOIN.
  2. 查询 2 是正确的,因为无论数据质量/条件如何,您都希望查看所有订单。
  3. 一般来说,我喜欢避免使用RIGHT JOINs,因为它会让一些开发人员感到困惑,因此可读性较差。LEFT JOIN您通常可以通过有效使用s来编写查询以执行相同的操作。
  4. 查询 2 是我对这样简单的事情的建议。
  5. 一个一般规则...一旦您OUTER JOIN在查询中引入 an,后面的JOINs 也应该是OUTER JOINs。否则,您可以排除您不想要的行。
于 2013-05-08T14:28:11.960 回答
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
于 2013-05-08T14:34:08.150 回答
2

查询 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
于 2013-05-08T14:22:09.553 回答
2

1)它不起作用,因为当您INNER JOIN要求Companies它在整个联接中存在时,但由于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)这更容易用谷歌搜索来回答。我敢肯定,我可以在几分钟内写出更全面的信息。

于 2013-05-08T14:29:23.477 回答