如何在SQl Server中进行左外部联接?

时间:2020-02-27 17:28:04

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

首先:我知道使用所有类型的联接,但是我不知道为什么该查询对这种查询如此工作

我有一个通过使用3个表以及在销售和订购商品之间进行左外部联接来进行SQL查询的方案。

我的桌子

--------------------
   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

Out put My Code

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

4 个答案:

答案 0 :(得分: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

答案 1 :(得分:0)

这是重复的,因为您在Invoice_Item和Item_Order之间没有辅助关联。对于Invoice_Item中的每个记录,它仅基于Item ID匹配到Item_Order(称为笛卡尔结果)。因此,您的订单数量显示为1:1引用,因此第一个发票项目数量10为MEANT,将与Item_Order Box = 30关联。数量8为MEANT,与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匹配。

发生的是发票项目中的每个项目都根据项目ID连接到Item_Order。所以你得到两个。如果您有3个发票项目,分别具有Items_Order的1和6,则将获得18行。

反馈

即使您已经收到基于OVER / PARTITION / ROW NUMBER的答案,该过程仍在向每行强制添加辅助ID。对于整个数据结构关联而言,依靠这种方法并不是最好的选择。如果删除订单上的第二个项目,会发生什么。您确定要删除invoice_items中的第二个项目吗?

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

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

因此,现在它联接到“发票物料”表,并且由于您的条件,它仅基于物料联接。因此,它看到第一行并说...是的,这是项目1,因此将其与项目订单记录一起包含在内(返回的第一行)。现在,它转到发票项目表的第二行...是的,它也与项目1相同,因此它又返回了它(返回了第二行)。

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

返回到“发票项目”表,并执行完全相同的测试...,并针对具有项目= 1的项目订单中的每一行,以及第三和第四行,因此您加倍...如果任一表中都有更多记录使用相同的商品ID,它将返回更多记录... 3和3条记录将返回9行。 4和4记录将返回16行,依此类推。进行替代可以工作,但是我认为没有更好/更新的设计结构那么安全。

答案 2 :(得分:0)

因为当您第一次将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 :(得分:0)

您正在连接一个键-在一个表中使用同一键的两行乘以第二个表中的两行= 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

如果您知道两个表(给定项目)的行数​​相同,则可以使用内部联接,而不必使用括号。