2011-02-04 7 views
1

Dans SQL Server, j'ai un jeu de résultats à partir d'une relation multiple: many join.SQL - identifier les lignes pour une valeur dans une table, où toutes les lignes jointes ont seulement une valeur spécifique

Considérant les produits liés aux commandes via une table de lien,

Table - Products 
ID 
ProductName 

Table - Orders 
ID 
OrderCountry 

LinkTable OrderLines (columns not shown) 

Je voudrais être en mesure de filtrer ces résultats pour afficher uniquement les résultats où pour une entité d'une table, toutes les valeurs de la l'autre table n'a qu'une valeur donnée dans une colonne particulière. En ce qui concerne mon exemple, pour chaque produit, je veux revenir seulement les lignes jointes lorsque tous les ordres qu'ils sont liés à sont pour le pays « uk »

Donc, si mon jeu de résultats liés est

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

Je veux filtrer de sorte que seuls les produits qui ont seulement été commandés au Royaume-Uni soient montrés (c.-à-d. Chocolat). Je suis sûr que cela devrait être simple, mais son vendredi après-midi et la partie SQL de mon cerveau a abandonné pour le jour ...

Répondre

3

Vous pouvez faire quelque chose comme ça, où le premier vous obtenez tous les produits vendus uniquement dans un seul pays, alors vous procéder pour obtenir toutes les commandes pour ces produits

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

Cela me semble assez logique - merci - je vais avoir un travail avec cela aussi et voir comment je m'entends. –

+0

J'ai essayé ceci avec mon jeu de données réel, et en ce moment j'ai l'air bien - je ferai un peu plus de vérification avant de confirmer. Merci pour votre aide –

+0

Triés - merci –

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

Renvoie 'Banana', qui ne doit pas être retourné – AakashM

+0

Désolé - cela retourne la ligne de produit qui a une commande usa - j'ai besoin de filtrer cette ligne (et d'autres comme dans mon jeu de données réel ...) –

1

Hmm. Sur la base de l'approche antérieure de Philip, essayez d'ajouter quelque chose comme ceci pour exclure les lignes où il y a eu le même produit commandé dans un autre pays:

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

Actuellement avec cela, en utilisant les tables dans mon exemple, je reçois " Msg 4104, niveau 16, état 1, ligne 1 L'identificateur en plusieurs parties "Products.ID" n'a pas pu être lié. " pour la clause finale où. –

+1

@Kris Eh bien, ouais, j'essayais juste de vous donner l'idée. Mais si c'est tout ce qui ne va pas, changez Products.ID en pr.ID, parce que c'est comme ça que c'est aliasé dans la requête principale. Je vais changer la réponse. –

+0

Je suis désolé - Je ne voulais pas paraître ingrat - J'ai regardé cela pendant trop longtemps et je ne pouvais pas repérer pourquoi l'alias avait tort dans votre requête - Je vais revoir cela maintenant –

1

Dans l'espoir que certaines données peuvent être généralement réutilisables:

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

Dans le serveur de démarrage, vous définissez la requête que vous avez actuellement pour générer les résultats intermédiaires que vous avez affichés. Le countryRankings CTE ajoute deux nouvelles colonnes, qui classe les pays dans chaque produit. Le SingleCountry CTE réduit le résultat ramené aux résultats où le pays est à la fois le premier et le dernier pays du produit (c'est-à-dire qu'il n'y a qu'un seul pays pour ce produit). Enfin, nous demandons les résultats qui viennent du Royaume-Uni.

Si vous voulez, par exemple, toutes les lignes de ProductID avec un seul pays d'origine, vous passez cette dernière clause where (et vous obtiendrez 3, fraise, 4, USA dans vos résultats aussi)


vous est donc avez une requête en cours qui ressemble à:

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) 

alors vous récrire le premier CTE comme:

;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

Cela donne le résultat correct pour mon exemple de jeu de données - mon souci de mise à l'échelle est que j'ai réellement des milliers de 'produits', donc je ne peux pas les lister tous dans les unions initiales - désolé si je ne le fais pas la question originale –

+0

@Kris C - mon intention était que parmi les parenthèses pour startingRS, vous mettez n'importe quelle requête actuelle qui génère votre ensemble de résultats liés - je viens de faire les trucs union ci-dessus parce que je n'ai pas vos vraies tables et échantillon de données –

+0

ah ok - je vois que je vais avoir un jeu avec cela - merci –

0

Ce n'est probablement pas le moyen le plus efficace de le faire, mais ...

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' 
Questions connexes