如何在 SQl 服务器中使用 left outer join?
How to work left outer join in SQl Server?
首先:我知道可以使用所有类型的连接,但是我不知道为什么这个查询会这样工作
我有一个用于进行 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?或者为什么我的输出 return 4 条记录?
这是重复的,因为您在 Invoice_Item 和 Item_Order 之间没有二级关联。对于 Invoice_Item 中的每条记录,它仅根据项目 ID 匹配 Item_Order(称为笛卡尔结果)。因此,您的订单数量似乎是一个 1:1 参考,因此第一个发票项目数量 10 意味着与 Item_Order Box = 30 相关联。而数量 8 意味着与 Item_Order 盒子 = 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 个发票项目,其中 1 个和 6 个 Items_Order,您将得到 18 行。
反馈
即使您有一个基于 OVER/PARTITION/ROW NUMBER 的可接受答案,该过程也会强制为每一行提供一个替代辅助 ID。依赖这种方法对于整体数据结构关联来说并不是最好的。如果您删除订单中的第二个项目会发生什么。您确定要删除 invoice_items 中的第二项吗?
至于在原始场景中 returning 2 条记录,您可以通过代理过程,但我认为从长远来看,了解连接上发生的事情对您来说会更好。回到 Item_Order 和 Invoice_Item 的样本数据。那么让我们从 Item_Order table 开始。 SQL 引擎将单独处理每一行。
第一行 SQL 抓取 Item = 1,Box = 30,Qty = 15000。
所以现在它加入了发票项目 table,并且由于您的标准它只根据项目加入。因此,它看到第一行并说...是的,这是第 1 项,因此请将其包含在项目订单记录中(第一行 returned)。现在它转到发票项目的第二行 table... 是的,它也是同一个项目 1,所以它再次 return 了(第二行 returned)。
现在,SQL 抓取第二行 Item = 1,Box = 12,Qty = 6000。
返回发票项目 table 并进行完全相同的测试...并且对于项目订单中项目 = 1 的每一行,以及第 3 行和第 4 行,因此您加倍...如果 table 中有更多记录具有相同的项目 ID,则将 return 更多记录...3 和 3 记录将有 return 9 行。 4 和 4 条记录会 return 16 行,等等。做代理会起作用,但我认为不如 better/updated 设计结构安全。
您的结果可以通过 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
您可以像这样添加更多表格:
left outer join (select Item
, Num
, Quantity
, row_number () over (order by Item) rn
from Invoice_Item ) b
因为当您第一次加入 Item
和 Item_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
合并
您正在按一个键加入 - 两行具有相同的键,其中一个 table 乘以第二个中的两行 table = 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
子句中使用圆括号的少数情况之一。此代码可以处理 table 的 either 中的其他行——如果一个 table 比另一个长,则另一个的列将是 NULL
.
如果您知道两个 table 具有相同的行数(对于给定的项目),您可以只使用内部联接而不使用括号。
首先:我知道可以使用所有类型的连接,但是我不知道为什么这个查询会这样工作
我有一个用于进行 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?或者为什么我的输出 return 4 条记录?
这是重复的,因为您在 Invoice_Item 和 Item_Order 之间没有二级关联。对于 Invoice_Item 中的每条记录,它仅根据项目 ID 匹配 Item_Order(称为笛卡尔结果)。因此,您的订单数量似乎是一个 1:1 参考,因此第一个发票项目数量 10 意味着与 Item_Order Box = 30 相关联。而数量 8 意味着与 Item_Order 盒子 = 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 个发票项目,其中 1 个和 6 个 Items_Order,您将得到 18 行。
反馈
即使您有一个基于 OVER/PARTITION/ROW NUMBER 的可接受答案,该过程也会强制为每一行提供一个替代辅助 ID。依赖这种方法对于整体数据结构关联来说并不是最好的。如果您删除订单中的第二个项目会发生什么。您确定要删除 invoice_items 中的第二项吗?
至于在原始场景中 returning 2 条记录,您可以通过代理过程,但我认为从长远来看,了解连接上发生的事情对您来说会更好。回到 Item_Order 和 Invoice_Item 的样本数据。那么让我们从 Item_Order table 开始。 SQL 引擎将单独处理每一行。
第一行 SQL 抓取 Item = 1,Box = 30,Qty = 15000。
所以现在它加入了发票项目 table,并且由于您的标准它只根据项目加入。因此,它看到第一行并说...是的,这是第 1 项,因此请将其包含在项目订单记录中(第一行 returned)。现在它转到发票项目的第二行 table... 是的,它也是同一个项目 1,所以它再次 return 了(第二行 returned)。
现在,SQL 抓取第二行 Item = 1,Box = 12,Qty = 6000。
返回发票项目 table 并进行完全相同的测试...并且对于项目订单中项目 = 1 的每一行,以及第 3 行和第 4 行,因此您加倍...如果 table 中有更多记录具有相同的项目 ID,则将 return 更多记录...3 和 3 记录将有 return 9 行。 4 和 4 条记录会 return 16 行,等等。做代理会起作用,但我认为不如 better/updated 设计结构安全。
您的结果可以通过 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
您可以像这样添加更多表格:
left outer join (select Item
, Num
, Quantity
, row_number () over (order by Item) rn
from Invoice_Item ) b
因为当您第一次加入 Item
和 Item_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
您正在按一个键加入 - 两行具有相同的键,其中一个 table 乘以第二个中的两行 table = 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
子句中使用圆括号的少数情况之一。此代码可以处理 table 的 either 中的其他行——如果一个 table 比另一个长,则另一个的列将是 NULL
.
如果您知道两个 table 具有相同的行数(对于给定的项目),您可以只使用内部联接而不使用括号。