SQL—在一个表中为一个值标识行,其中所有连接行只有一个特定值
在SQL Server中,我有一个多:多关联关系的结果集 考虑通过链接表链接到订单的产品SQL—在一个表中为一个值标识行,其中所有连接行只有一个特定值,sql,sql-server,Sql,Sql Server,在SQL Server中,我有一个多:多关联关系的结果集 考虑通过链接表链接到订单的产品 Table - Products ID ProductName Table - Orders ID OrderCountry LinkTable OrderLines (columns not shown) 我希望能够过滤这些结果,只显示结果,其中对于一个表中的实体,另一个表中的所有值在特定列中只有一个给定值。在我的示例中,对于每种产品,我只想在它们链接到的所有订单都是针对国家“uk”时返回连接的行
Table - Products
ID
ProductName
Table - Orders
ID
OrderCountry
LinkTable OrderLines (columns not shown)
我希望能够过滤这些结果,只显示结果,其中对于一个表中的实体,另一个表中的所有值在特定列中只有一个给定值。在我的示例中,对于每种产品,我只想在它们链接到的所有订单都是针对国家“uk”时返回连接的行
所以如果我的链接结果集是
productid, product, orderid, ordercountry
1, Chocolate, 1, uk
2, Banana, 2, uk
2, Banana, 3, usa
3, Strawberry, 4, usa
我想过滤,以便只显示那些仅在英国订购的产品(即巧克力)。我相信这应该是直截了当的,但现在是周五下午,我大脑中的SQL部分已经放弃了一天…嗯。根据Philip先前的方法,尝试添加类似的内容,以排除在其他国家订购相同产品的行:
SELECT pr.Id, pr.ProductName, od.Id, od.OrderCountry
from Products pr
inner join LinkTable lt
on lt.ProductId = pr.ID
inner join Orders od
on od.ID = lt.OrderId
where od.OrderCountry = 'UK'
SELECT pr.Id, pr.ProductName, od.Id, od.OrderCountry
from Products pr
inner join LinkTable lt
on lt.ProductId = pr.ID
inner join Orders od
on od.ID = lt.OrderId
where
od.OrderCountry = 'UK'
AND NOT EXISTS
(
SELECT
*
FROM
Products MatchingProducts
inner join LinkTable lt
on lt.ProductId = MatchingProducts.ID
inner join Orders OrdersFromOtherCountries
on OrdersFromOtherCountries.ID = lt.OrderId
WHERE
MatchingProducts.ID = Pr.ID AND
OrdersFromOtherCountries.OrderCountry != od.OrderCountry
)
嗯。根据Philip早期的方法,尝试添加类似的内容,以排除在其他国家订购相同产品的行:
SELECT pr.Id, pr.ProductName, od.Id, od.OrderCountry
from Products pr
inner join LinkTable lt
on lt.ProductId = pr.ID
inner join Orders od
on od.ID = lt.OrderId
where
od.OrderCountry = 'UK'
AND NOT EXISTS
(
SELECT
*
FROM
Products MatchingProducts
inner join LinkTable lt
on lt.ProductId = MatchingProducts.ID
inner join Orders OrdersFromOtherCountries
on OrdersFromOtherCountries.ID = lt.OrderId
WHERE
MatchingProducts.ID = Pr.ID AND
OrdersFromOtherCountries.OrderCountry != od.OrderCountry
)
希望其中一些可以普遍重复使用:
;with startingRS (productid, product, orderid, ordercountry) as (
select 1, 'Chocolate', 1, 'uk' union all
select 2, 'Banana', 2, 'uk' union all
select 2, 'Banana', 3, 'usa' union all
select 3, 'Strawberry', 4, 'usa'
), countryRankings as (
select productid,product,orderid,ordercountry,
RANK() over (PARTITION by productid ORDER by ordercountry) as FirstCountry,
RANK() over (PARTITION by productid ORDER by ordercountry desc) as LastCountry
from
startingRS
), singleCountry as (
select productid,product,orderid,ordercountry
from countryRankings
where FirstCountry = 1 and LastCountry = 1
)
select * from singleCountry where ordercountry='uk'
在startingRS中,您可以放置当前必须的任何查询来生成显示的中间结果。countryRankings CTE添加了两个新列,对每个productid中的国家进行排名
singleCountry CTE将结果集缩减为productid中第一个和最后一个国家的国家(即此productid只有一个国家)。最后,我们查询那些来自英国的结果
例如,如果希望所有productid行都有一个原产国,只需跳过最后一个where子句(结果中也会有3,草莓,4,美国)
因此,您当前的查询如下所示:
select p.productid,p.product,o.orderid,o.ordercountry
from product p inner join order o on p.productid = o.productid --(or however these joins work for your tables)
然后将第一个CTE重写为:
;with startingRS (productid, product, orderid, ordercountry) as (
select p.productid,p.product,o.orderid,o.ordercountry
from product p inner join order o on p.productid = o.productid
), /* rest of query */
希望其中一些可以普遍重复使用:
;with startingRS (productid, product, orderid, ordercountry) as (
select 1, 'Chocolate', 1, 'uk' union all
select 2, 'Banana', 2, 'uk' union all
select 2, 'Banana', 3, 'usa' union all
select 3, 'Strawberry', 4, 'usa'
), countryRankings as (
select productid,product,orderid,ordercountry,
RANK() over (PARTITION by productid ORDER by ordercountry) as FirstCountry,
RANK() over (PARTITION by productid ORDER by ordercountry desc) as LastCountry
from
startingRS
), singleCountry as (
select productid,product,orderid,ordercountry
from countryRankings
where FirstCountry = 1 and LastCountry = 1
)
select * from singleCountry where ordercountry='uk'
在startingRS中,您可以放置当前必须的任何查询来生成显示的中间结果。countryRankings CTE添加了两个新列,对每个productid中的国家进行排名
singleCountry CTE将结果集缩减为productid中第一个和最后一个国家的国家(即此productid只有一个国家)。最后,我们查询那些来自英国的结果
例如,如果希望所有productid行都有一个原产国,只需跳过最后一个where子句(结果中也会有3,草莓,4,美国)
因此,您当前的查询如下所示:
select p.productid,p.product,o.orderid,o.ordercountry
from product p inner join order o on p.productid = o.productid --(or however these joins work for your tables)
然后将第一个CTE重写为:
;with startingRS (productid, product, orderid, ordercountry) as (
select p.productid,p.product,o.orderid,o.ordercountry
from product p inner join order o on p.productid = o.productid
), /* rest of query */
这可能不是最有效的方法,但是
SELECT p.ProductName
FROM Product p
WHERE p.ProductId IN
(
SELECT DISTINCT ol.ProductId
FROM OrderLines ol
INNER JOIN [Order] o
ON ol.OrderId = o.OrderId
WHERE o.OrderCountry = 'uk'
)
AND p.ProductId NOT IN
(
SELECT DISTINCT ol.ProductId
FROM OrderLines ol
INNER JOIN [Order] o
ON ol.OrderId = o.OrderId
WHERE o.OrderCountry != 'uk'
)
测试数据
create table product
(
ProductId int,
ProductName nvarchar(50)
)
go
create table [order]
(
OrderId int,
OrderCountry nvarchar(50)
)
go
create table OrderLines
(
OrderId int,
ProductId int
)
go
insert into Product VALUES (1, 'Chocolate')
insert into Product VALUES (2, 'Banana')
insert into Product VALUES (3, 'Strawberry')
insert into [order] values (1, 'uk')
insert into [order] values (2, 'uk')
insert into [order] values (3, 'usa')
insert into [order] values (4, 'usa')
insert into [orderlines] values (1, 1)
insert into [orderlines] values (2, 2)
insert into [orderlines] values (3, 2)
insert into [orderlines] values (4, 3)
insert into [orderlines] values (3, 2)
insert into [orderlines] values (3, 3)
这可能不是最有效的方法,但是
SELECT p.ProductName
FROM Product p
WHERE p.ProductId IN
(
SELECT DISTINCT ol.ProductId
FROM OrderLines ol
INNER JOIN [Order] o
ON ol.OrderId = o.OrderId
WHERE o.OrderCountry = 'uk'
)
AND p.ProductId NOT IN
(
SELECT DISTINCT ol.ProductId
FROM OrderLines ol
INNER JOIN [Order] o
ON ol.OrderId = o.OrderId
WHERE o.OrderCountry != 'uk'
)
测试数据
create table product
(
ProductId int,
ProductName nvarchar(50)
)
go
create table [order]
(
OrderId int,
OrderCountry nvarchar(50)
)
go
create table OrderLines
(
OrderId int,
ProductId int
)
go
insert into Product VALUES (1, 'Chocolate')
insert into Product VALUES (2, 'Banana')
insert into Product VALUES (3, 'Strawberry')
insert into [order] values (1, 'uk')
insert into [order] values (2, 'uk')
insert into [order] values (3, 'usa')
insert into [order] values (4, 'usa')
insert into [orderlines] values (1, 1)
insert into [orderlines] values (2, 2)
insert into [orderlines] values (3, 2)
insert into [orderlines] values (4, 3)
insert into [orderlines] values (3, 2)
insert into [orderlines] values (3, 3)
你可以这样做,首先你得到的所有产品只在一个国家销售,然后你继续得到这些产品的所有订单
with distinctProducts as
(
select LinkTable.ProductID
from Orders
inner join LinkTable on LinkTable.OrderID = Orders.ID
group by LinkTable.ProductID
having count(distinct Orders.OrderCountry) = 1
)
select pr.ID as ProductID
,pr.ProductName
,o.ID as OrderID
,o.OrderCountry
from Products pr
inner join LinkTable lt on lt.ProductID = pr.ID
inner join Orders o on o.ID = lt.OrderID
inner join distinctProducts dp on dp.ProductID = pr.ID
where o.OrderCountry = 'UK'
你可以这样做,首先你得到的所有产品只在一个国家销售,然后你继续得到这些产品的所有订单
with distinctProducts as
(
select LinkTable.ProductID
from Orders
inner join LinkTable on LinkTable.OrderID = Orders.ID
group by LinkTable.ProductID
having count(distinct Orders.OrderCountry) = 1
)
select pr.ID as ProductID
,pr.ProductName
,o.ID as OrderID
,o.OrderCountry
from Products pr
inner join LinkTable lt on lt.ProductID = pr.ID
inner join Orders o on o.ID = lt.OrderID
inner join distinctProducts dp on dp.ProductID = pr.ID
where o.OrderCountry = 'UK'
将返回
Banana
,不返回抱歉-返回有美国订单的产品行-我需要过滤掉这一行(以及我真实数据集中的其他类似行…)将返回Banana
,不返回抱歉-返回有美国订单的产品行-我需要过滤掉这一行(以及其他类似于我的真实数据集中的数据…)目前,使用我的示例中的表,我得到“Msg 4104,16级,状态1,第1行多部分标识符”Products.ID“无法绑定”对于最后一个where子句。@Kris好吧,是的,我只是想告诉你这个想法。但是如果这都是错的,那么就把Products.ID改为pr.ID,因为这就是它在主查询中的别名。我会改变答案。我很抱歉-我并不是故意说我忘恩负义-我看这个问题太久了,无法找出别名错误的原因在你的询问中,我将重新讨论这个问题now@Kris不用担心。基本上,子查询有自己的产品、订单和链接表的副本,以完成与主查询非常类似的工作。通过基于相同的产品链接产品副本(MatchingProducts.ID=Pr.ID)以及基于不同国家/地区的OrderCountry副本(OrdersFromOtherCountries.OrderCountry!=od.OrderCountry),并且使用和不存在,我排除了主查询中的所有内容,其中有一个匹配的产品在不同的国家销售。希望这相当清楚;这些东西不容易解释…感谢您的帮助-我相信这会让我达到目的,但Alex的回答首先为我排序-两种方法都很有意义现在,使用我的示例中的表,我得到了“Msg 4104,level16,state1,Line 1”多部分标识符“Products.ID”无法绑定对于最后一个where子句。@Kris好吧,是的,我只是想告诉你这个想法。但是如果这都是错的,那么就把Products.ID改为pr.ID,因为这就是它在主查询中的别名。我会改变答案。我很抱歉-我并不是故意说我忘恩负义-我看这个问题太久了,无法找出别名错误的原因在你的询问中,我将重新讨论这个问题now@Kris不用担心。基本上,子查询有自己的产品、订单和链接表的副本,以完成与主查询非常类似的工作。通过基于相同的产品链接产品副本(MatchingProducts.ID=Pr.ID)而OrderCountry的副本基于不同的国家(OrdersFromOtherCountries.OrderCountry!=od.OrderCountry),并且使用且不存在,我排除了所有内容