繁体   English   中英

SQL-在一个表中标识值的行,其中所有联接的行仅具有特定值

[英]SQL - identifying rows for a value in one table, where all joined rows only has a specific value

在SQL Server中,我有一个加入了many:many关系的结果集。

考虑通过链接表链接到订单的产品,

Table - Products
ID
ProductName

Table - Orders
ID
OrderCountry

LinkTable OrderLines (columns not shown)

我希望能够过滤这些结果,以仅显示对于一个表中的实体,另一表中的所有值在特定列中仅具有给定值的结果。 就我的示例而言,对于每种产品,当它们链接到的所有订单都针对国家/地区时,我只想返回联接的行

因此,如果我的链接结果集是

productid, product, orderid, ordercountry
1, Chocolate, 1, uk
2, Banana, 2, uk
2, Banana, 3, usa
3, Strawberry, 4, usa

我要过滤,以便仅显示那些仅在英国订购的产品(即巧克力)。 我敢肯定这应该是直接的,但是今天星期五下午和我大脑的SQL部分已经放弃了……

您可以执行以下操作,首先获得仅在一个国家/地区销售的所有产品,然后继续获取这些产品的所有订单

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'

根据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将结果集缩减为那些国家/地区在生产ID中既是第一个国家又是最后一个国家(即,该生产ID中只有一个国家)的结果。 最后,我们查询仅来自英国的结果。

例如,如果您想要所有具有单个原产国的productid行,则只需跳过最后一个where子句(结果中还会得到3,strawberry,4,usa)。


因此,您是否有一个当前查询,如下所示:

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 mytable (productid,ordercountry)
AS
(SELECT productid, ordercountry
 FROM Orders od INNER JOIN LinkTable lt ON od.orderid = lt.OrderId) 

SELECT * FROM mytable
INNER JOIN dbo.Products pr ON pr.productid = mytable.productid
WHERE pr.productid NOT IN (SELECT productid FROM mytable
                           GROUP BY productid
                           HAVING COUNT(ordercountry) > 1)
AND 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'

这可能不是最有效的方法,但是...

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)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM