2011-02-04 3 views
1

В SQL Server у меня есть результирующий набор из объединенного отношения many: many.SQL - идентификационные строки для значения в одной таблице, где все связанные строки имеют только определенное значение

Учитывая продукты, связанные с Заказы через таблицу ссылок,

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-часть моего мозга отказались от дня ...

ответ

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' 
+0

Это кажется мне довольно логичным - спасибо - у меня тоже будет работа с этим и посмотреть, как я нахожусь. –

+0

Я пробовал это с моим реальным набором данных, и в настоящий момент выгляжу хорошо - сделаю немного больше проверки, прежде чем подтвердить. Спасибо за вашу помощь –

+0

Сортировка - спасибо –

0
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' 
+1

Вернется 'Банан', который не возвращается – AakashM

+0

Извините - это возвращает строку продукта, у которой есть USA-заказ - мне нужно отфильтровать эту строку (и другие подобные мне в моем реальном наборе данных ...) –

1

Хмм. На основе ранее подхода Филиппа, попробуйте добавить что-то вроде этого, чтобы исключить строки, где был тот же самый продукт заказал в другой стране:

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 
    ) 
+0

В настоящее время с этим, используя таблицы в моем примере, я получаю " Msg 4104, уровень 16, состояние 1, строка 1 Идентификатор из нескольких частей «Products.ID» не может быть связан ». для финальной статьи. –

+1

@ Kris Ну, да, я просто пытался дать вам эту идею. Но если все это не так, замените Products.ID на pr.ID, потому что это то, как он сглажен в основном запросе. Я изменю ответ. –

+0

Извините, я не хотел звучать неблагодарно. Я слишком долго смотрел на это и не мог понять, почему псевдоним был неправильным в вашем запросе. Сейчас я вернусь к этому вопросу. –

1

В надежде, что некоторые из этого может быть в целом многоразовые:

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

В startRS вы помещаете любой запрос, который у вас есть, для создания промежуточных результатов, которые вы показывали. CountryRankings CTE добавляет два новых столбца, которые оценивают страны в каждом продукте.

SingleCountry CTE уменьшает результирующий набор вплоть до тех результатов, в которых страна считается первой и последней страной в продукте (т. Е. Для этого продукта существует только одна страна). Наконец, мы запрашиваем те результаты, которые находятся только от uk.

Если вы хотите, например, вся ProductID строка с одной страной происхождения, просто пропустите этот последний пункт, где (и вы получите 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) 

Тогда вы бы переписать первый КТР как:

;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 */ 
+0

Это дает правильный результат для моего набора данных примера - мое беспокойство по поводу масштабирования состоит в том, что на самом деле у меня есть 1000 «продуктов», поэтому явным образом не могу их перечислить в начальных объединениях - извините, если я не сделал это ясно в исходный вопрос –

+0

@Kris C - мое намерение заключалось в том, что в скобках для startRS вы помещаете любой текущий запрос, который у вас есть, который генерирует ваш связанный результирующий набор - я просто сделал это выше, потому что у меня нет реальных таблиц и Пример данных –

+0

ah ok - Я вижу, что у меня будет игра с этим - спасибо –

0

Это, вероятно, не самый эффективный способ сделать это, но ...

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

TestData

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) 
1
;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' 
Смежные вопросы