【问题标题】:How to work left outer join in SQl Server?如何在 SQL Server 中使用左外连接?
【发布时间】:2020-06-11 19:01:39
【问题描述】:

首先:我知道要使用所有类型的连接,但我不知道为什么这个查询会这样工作

我有一个用于制作 SQL 查询的场景,方法是使用 3 个表和销售商品和订单商品之间的左外连接。

我的表格

--------------------
   Item 
--------------------
ID      |  Code
--------------------
1       |  7502

SQL > select * from Item where id = 1
---------------------

   Item_Order
---------------------------
Item   |  Box   |   Quantity
---------------------------
1      | 30     |  15000
1      | 12     |  6000
SQL > select * from Item_Order where Item = 1
--------------------------

   Invoice_Item
-------------------
Item  |  Num  |  Quantity
-------------------------
1     | 1.64  | 10
1     | 2.4   | 8
SQL > select * from Invoice_Item where Item = 1

我想要这个输出:

Item  | OrderQ  | OrderB | SellN | SellQ
-----------------------------------------
1     | 1500    | 30     |  1.64 | 10
1     | 6000    | 12     |  2.4  | 8

我的 SQL 代码:

SELECT  Item.ID, Item_Order.Box As OrderB, Item_Order.Quantity As OrderQ, Invoice_Item.Num As SellN, Invoice_Item.Quantity As SellQ
FROM Item LEFT OUTER JOIN 
     Invoice_Item ON Item.ID = Invoice_Item.Item LEFT OUTER JOIN 
     Item_Order ON Item_Order.Item = Item.ID  
where Item.ID = 1

为什么我的输出是 2x?或者为什么我的输出返回 4 条记录?

【问题讨论】:

  • 您的产量翻了一番,因为您没有提供足够的订单和发票之间的关系。 SQL 应该如何知道哪个发票对应哪个订单?
  • 要正确编写查询,需要了解您的架构。为此,我们需要了解您拥有的表之间的关系是什么——这些关系是通过约束强制执行的。显示 DDL。接下来,我们需要了解您的目标。如果我们不知道您要完成什么,“不起作用”的查询就不是很有用。
  • @SM我可以通过更改 sql 查询返回 2 条记录吗?
  • 这并不是一个真正的外连接问题。第一次连接产生两行。对于这些行中的每一行,第二个连接会再创建两个。
  • @SMor 约束,包括 FK——“表之间的关系 [原文如此]”——不需要查询。表的含义是充分和必要的。 (基础或结果)查询表示关系(船)/关联。当约束成立时,一些表达式变得等价于独立于约束的正确查询。我们确实需要知道约束来解决依赖于它们的查询表达式。

标签: sql sql-server left-join outer-join


【解决方案1】:

你的结果可以通过row_number实现:

select a.ID
       , a.OrderB
       , a.OrderQ
       , b.Quantity SellQ
       , b.Num SellN
from 
(SELECT Item.ID
       , Item_Order.Box As OrderB
       , Item_Order.Quantity As OrderQ
       , row_number () over (order by Item.ID) rn
FROM Item 
left outer JOIN Item_Order ON Item.ID = Item_Order.Item) a
left outer join (select Item
                        , Num
                        , Quantity
                        , row_number () over (order by Item) rn 
                 from Invoice_Item ) b
on a.ID = b.Item
and a.rn = b.rn

Here is a demo

您可以像这样添加更多表格:

left outer join (select Item
                            , Num
                            , Quantity
                            , row_number () over (order by Item) rn 
                     from Invoice_Item ) b

【讨论】:

  • 感谢您的 sql 代码,但您能解释一下吗?如果像 itemOrder 设计一样添加表 itemTrash 会发生什么?
  • 使用 row_number 您可以再添加一列可用于加入,这样您就可以删除重复项。请检查 row_number 是如何工作的。对于您问题的第二部分:“如果像 itemOrder 设计一样添加表 itemTrash 会发生什么?” - 我不明白你....请解释一下
  • 还有@ZanyarJ.Ahmed 欢迎您,我很乐意提供帮助。您可以投票并接受答案。在这里查看:stackoverflow.com/help/someone-answers.
  • 第二个问题,我想加入新的 Table neme “itemTrash columns item, qnt” 如何加入你的查询?
  • 我相信我已经为您的问题添加了答案。请检查我更新的答案。您可以添加更多这样的表。与我添加 Invoice_Item 表的方式相同。您也可以像这样添加表 Item_Order。这只是一种方法,每个新表都可以添加到我添加了表 Invoice_Item 的样式中。希望这会有所帮助。
【解决方案2】:

因为当您第一次将ItemItem_Order 连接时,它会输出两条记录,因为Item_Order 中有两条记录。现在这个结果查询将与Invoice_Item 保持连接,并且两条记录将与Invoice_Item 的所有记录连接

你可以这样理解

SELECT  Item.ID, Item_Order.Box As OrderB, Item_Order.Quantity As OrderQ, Invoice_Item.Num As SellN, Invoice_Item.Quantity As SellQ
FROM Item LEFT OUTER JOIN 
Invoice_Item ON Item.ID = Invoice_Item.Item LEFT OUTER JOIN 
where Item.ID = 1 into table4 //Only to explain

现在table4 的第一个查询结果将与Items_Order 连接

【讨论】:

    【解决方案3】:

    您在一个键上加入 - 一个表中具有相同键的两行乘以第二个表中的两行 = 4 行。

    您需要一个单独的密钥。您可以使用row_number() 生成一个:

    SELECT i.ID, io.Box As OrderB, io.Quantity As OrderQ,
           ii.Num As SellN, ii.Quantity As SellQ
    FROM Item i LEFT OUTER JOIN 
         ((SELECT ii.*,
                  ROW_NUMBER() OVER (PARTITION BY ii.item ORDER BY ii.item) as seqnum
           FROM Invoice_Item ii
          ) FULL JOIN
          (SELECT io.*, 
                  ROW_NUMBER() OVER (PARTITION BY io.item ORDER BY io.item) as seqnum
           FROM Item_Order io
          ) io
          ON io.Item = ii.ID AND io.seqnum = ii.seqnum
         )
         ON i. = ii.Item 
    where i.ID = 1;
    

    请注意,这是我在FROM 子句中使用括号的少数情况之一。这段代码可以处理任意一个表中的额外行——如果一个表比另一个长,另一个表的列将是NULL

    如果您知道这两个表的行数相同(对于给定项目),您可以只使用内连接而不使用括号。

    【讨论】:

      【解决方案4】:

      这是重复的,因为您在 Invoice_Item 和 Item_Order 之间没有二级关联。对于 Invoice_Item 中的每条记录,它仅根据项目 ID 与 Item_Order(称为笛卡尔结果)匹配。因此,您的订单数量显示为 1:1 参考,因此第一个发票项目数量 10 意味着与 Item_Order Box = 30 相关联。数量 8 意味着与 Item_Order Box = 12 相关联。

      Item_Order
      Item   Box   Quantity
      1      30    15000
      1      12    6000
      
      
      Invoice_Item
      Item  Num    Quantity
      1     1.64   10
      1     2.4    8
      

      您可能需要添加“Box”引用,以便 Item_Order 和 Invoice_Item 是 1:1 匹配的。

      Invoice Item 中的每个项目都根据项目 ID 加入到 Item_Order 中。所以你得到了两个。如果您有 3 个发票项目,其中 1 和 6 是 Items_Order,您将获得 18 行。

      反馈

      即使您有一个基于 OVER/PARTITION/ROW NUMBER 的已接受答案,该过程也会强制为每一行指定一个代理辅助 ID。依赖这种方法对于整体数据结构关联来说并不是最好的。如果您删除订单上的第二个项目会发生什么。您确定要删除 invoice_items 中的第二项吗?

      至于在原始场景中返回 2 条记录,您可以通过代理过程,但我认为长期了解联接中发生的情况对您来说会更好。回到 Item_Order 和 Invoice_Item 的示例数据。因此,让我们从 Item_Order 表开始。 SQL 引擎将单独处理每一行。

      第一行 SQL 抓取 Item = 1,Box = 30,Qty = 15000。

      所以现在它加入了 Invoice Item 表,并且由于您的标准它只基于 Item 加入。因此,它看到第一行并说...是的,这是第 1 项,因此将其包含在项目订单记录中(返回的第一行)。现在它转到发票项目表中的第二行......是的,它也是同一个项目 1,所以它再次返回它(返回第二行)。

      现在,SQL 抓取第二行 Item = 1, Box = 12, Qty = 6000。

      返回 Invoice Item 表并进行完全相同的测试...对于 Item Order 中具有 Item = 1 的每一行,第 3 行和第 4 行因此您的加倍...如果任一表有更多记录具有相同的项目 ID,它将返回更多的记录... 3 和 3 记录将返回 9 行。 4 条记录和 4 条记录将返回 16 行,依此类推。进行代理会起作用,但我认为不如更好/更新的设计结构安全。

      【讨论】:

      • 你的意思是不能永远为这种情况返回 2 条记录?
      • @ZanyarJ.Ahmed,对我的回答进行了澄清。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多