2009-07-21 8 views
5

J'essaie de résoudre le problème ci-dessous. Je me sens comme c'est possible, mais je n'arrive pas à l'obtenir.SQL Elaborate joint la requête

Voici le scénario:

Table 1 (Assets) 
1 Asset-A 
2 Asset-B 
3 Asset-C 
4 Asset-D 

Table 2 (Attributes) 
1 Asset-A Red 
2 Asset-A Hard 
3 Asset-B Red 
4 Asset-B Hard 
5 Asset-B Heavy 
6 Asset-C Blue 
7 Asset-C Hard 

Si je suis à la recherche de quelque chose ayant les mêmes attributs que l'actif-A, alors il devrait identifier des actifs B depuis Asset-B a tous les mêmes attributs que l'actif-A (il devrait se défausser lourd, puisque l'actif-A n'a pas précisé quelque chose de différent ou semblable). De plus, si je voulais que les attributs de l'actif A et de l'actif B soient communs, comment pourrais-je l'obtenir?

Cela semble simple, mais je ne peux pas le clouer ...

La table réelle que je me sers, est presque exactement Tableau2, simplement une association d'un assetId et un AttributeId donc: PK: Id
int: assetId
int: AttributeId

Je Comprise l'idée de la table d'actifs pour simplifier la question.

+3

Pourquoi -1? Question SQL parfaitement valide. +1 –

+0

Toute question qui ressemble à «Je ne sais pas comment les jointures fonctionnent» est plutôt intéressée et n'a pas d'utilité au-delà de l'affiche originale. –

+0

@Mark: cette question est * loin * au-delà de "comment fonctionnent les jointures". – Quassnoi

Répondre

0

Cette solution fonctionne comme prescrit, merci pour l'entrée.

WITH Atts AS 
(
    SELECT 
    DISTINCT 
     at1.[Attribute] 
    FROM 
     Attribute at1 
    WHERE 
     at1.[Asset] = 'Asset-A' 
) 

SELECT 
    DISTINCT 
    Asset, 
    (
     SELECT 
      COUNT(ta2.[Attribute]) 
     FROM 
      Attribute ta2 
     INNER JOIN 
      Atts b 
      ON 
       b.[Attribute] = ta2.[attribute] 
     WHERE 
      ta2.[Asset] = ta.Asset 
    ) 
    AS [Count] 
FROM 
    Atts a 
INNER JOIN 
    Attribute ta 
    ON 
    a.[Attribute] = ta.[Attribute] 
+0

Vous avez donc un attribut de table avec l'attribut de colonne? Avez-vous des valeurs NULL dans Attribut, car sinon COUNT (ta2. [Attribut]) n'est pas différent de COUNT (*) sauf qu'il est plus lent. La deuxième JOIN semble redondante. Pourtant, peut-être faire le travail :-)) – wqw

+0

Eh bien, ce ne sont pas les noms de tables réels, ou les noms de colonnes, juste des représentations rapides de ce que je suis en train de faire! – Praesidium

4
SELECT ato.id, ato.value 
FROM (
     SELECT id 
     FROM assets a 
     WHERE NOT EXISTS 
       (
       SELECT NULL 
       FROM attributes ata 
       LEFT JOIN 
         attributes ato 
       ON  ato.id = ata.id 
         AND ato.value = ata.value 
       WHERE ata.id = 1 
         AND ato.id IS NULL 
       ) 
     ) ao 
JOIN attributes ato 
ON  ato.id = ao.id 
JOIN attributes ata 
ON  ata.id = 1 
     AND ata.value = ato.value 

, ou SQL Server 2005 (avec des données d'échantillons pour vérifier):

WITH assets AS 
     (
     SELECT 1 AS id, 'A' AS name 
     UNION ALL 
     SELECT 2 AS id, 'B' AS name 
     UNION ALL 
     SELECT 3 AS id, 'C' AS name 
     UNION ALL 
     SELECT 4 AS id, 'D' AS name 
     ), 
     attributes AS 
     (
     SELECT 1 AS id, 'Red' AS value 
     UNION ALL 
     SELECT 1 AS id, 'Hard' AS value 
     UNION ALL 
     SELECT 2 AS id, 'Red' AS value 
     UNION ALL 
     SELECT 2 AS id, 'Hard' AS value 
     UNION ALL 
     SELECT 2 AS id, 'Heavy' AS value 
     UNION ALL 
     SELECT 3 AS id, 'Blue' AS value 
     UNION ALL 
     SELECT 3 AS id, 'Hard' AS value 
     ) 
SELECT ato.id, ato.value 
FROM (
     SELECT id 
     FROM assets a 
     WHERE a.id <> 1 
       AND NOT EXISTS 
       (
       SELECT ata.value 
       FROM attributes ata 
       WHERE ata.id = 1 
       EXCEPT 
       SELECT ato.value 
       FROM attributes ato 
       WHERE ato.id = a.id 
       ) 
     ) ao 
JOIN attributes ato 
ON  ato.id = ao.id 
JOIN attributes ata 
ON  ata.id = 1 
     AND ata.value = ato.value 
+0

Je n'arrive pas à faire fonctionner cela, je n'arrive pas à lier ata.Anything ... (L'identificateur en plusieurs parties "ata.AttributeId" n'a pas pu être lié.) – Praesidium

+0

@Praesidium: voir la mise à jour. – Quassnoi

0

Je ne comprends pas tout à fait la première partie de votre question, l'identification des actifs en fonction de leurs attributs.

Faire quelques hypothèses sur les noms de colonnes, la requête suivante donnerait les attributs communs entre l'actif-A et des actifs B:

SELECT [Table 2].Name 
FROM [Table 2] 
JOIN [Table 1] a ON a.ID = [Table 2].AssetID AND a.Name = 'Asset-A' 
JOIN [Table 1] b ON b.ID = [Table 2].AssetID AND b.Name = 'Asset-B' 
GROUP BY [Table 2].Name 
0
Select * From Assets A 
    Where Exists 
     (Select * From Assets 
     Where AssetId <> A.AssetID 
      And (Select Count(*) 
       From Attributes At1 Join Attributes At2 
        On At1.AssetId <> At2.AssetId 
         And At1.attribute <> At2.Attribute 
       Where At1.AssetId = A.AssetId Asset) = 0) 
    And AssetId = 'Asset-A' 
+0

Il devrait être là où n'existe pas, mais il retourne également les actifs qui n'ont pas d'attributs ... – Praesidium

+0

Ou actifs qui ont un seul attribut en commun ... Donc, il retournera Asset-C parce que c'est difficile, même si elle est pas bleu, comme Asset-A – Praesidium

0
select at2.asset, count(*) 
from  attribute at1 
inner join attribute at2 on at1.value = at2.value 
where at1.asset = "Asset-A" 
and at2.asset != "Asset-A" 
group by at2.asset 
having count(*) = (select count(*) from attribute where asset = "Asset-A"); 
+0

Cela ne semble pas retourner quelque chose ... Tous les actifs sont disqualifiés. – Praesidium

+0

Peut-être que c'est parce que j'ai utilisé "attributs" (au lieu de "attribut") pour le nom de la table dans la requête interne. Corrigée. Mais bien sûr, vous devez utiliser vos noms de table réels; Je ne sais pas ce qu'ils sont. –

0

Trouver tous les actifs qui ont tous les attributs que " A « a (mais peut également avoir des attributs supplémentaires):

SELECT Other.ID 
FROM Assets Other 
WHERE 
    Other.AssetID <> 'Asset-A' -- do not return Asset A as a match to itself 
    AND NOT EXISTS (SELECT NULL FROM Attributes AttA WHERE 
    AttA.AssetID='Asset-A' 
    AND NOT EXISTS (SELECT NULL FROM Attributes AttOther WHERE 
     AttOther.AssetID=Other.ID AND AttOther.AttributeID = AttA.AttributeID 
    ) 
    ) 

Ie, » trouver un élément d'actif où il n'y a pas d'attribut de A qui n'est pas aussi un attribut de cet actif ".

Trouver tous les actifs qui ont exactement les mêmes attributs que « A »:

SELECT Other.ID 
FROM Assets Other 
WHERE 
    Other.AssetID <> 'Asset-A' -- do not return Asset A as a match to itself 
    AND NOT EXISTS (SELECT NULL FROM Attributes AttA WHERE 
    AttA.AssetID='Asset-A' 
    AND NOT EXISTS (SELECT NULL FROM Attributes AttOther WHERE 
     AttOther.AssetID=Other.ID 
     AND AttOther.AttributeID = AttA.AttributeID 
    ) 
    ) 
    AND NOT EXISTS (SELECT NULL FROM Attributes AttaOther WHERE 
    AttaOther.AssetID=Other.ID 
    AND NOT EXISTS (SELECT NULL FROM Attributes AttaA WHERE 
     AttaA.AssetID='Asset-A' 
     AND AttaA.AttributeID = AttaOther.AttributeID 
    ) 
    ) 

Ie, « trouver un élément d'actif où il n'y a pas d'attribut de A qui n'est pas aussi un attribut de cet actif, et où il n'y a pas d'attribut de cet actif qui ne soit pas aussi un attribut de A. "

0

Trouvez tous les actifs qui ont tous les mêmes attributs que l'actif a:

select att2.Asset from attribute att1 
inner join attribute att2 on att2.Attribute = att1.Attribute and att1.Asset <> att2.Asset 
where att1.Asset = 'Asset-A' 
group by att2.Asset, att1.Asset 
having COUNT(*) = (select COUNT(*) from attribute where Asset=att1.Asset) 
0

Je pensais que je pourrais peut-être faire avec LINQ puis me frayer un chemin en arrière avec:

var result = from productsNotA in DevProducts 
      where productsNotA.Product != "A" && 
      (
       from productsA in DevProducts 
       where productsA.Product == "A" 
       select productsA.Attribute 
      ).Except 
      (
       from productOther in DevProducts 
       where productOther.Product == productsNotA.Product 
       select productOther.Attribute 
      ).Single() == null 
      select new {productsNotA.Product}; 

result.Distinct() 

Je pensais que traduire cela en SQL avec LinqPad se traduirait par une jolie requête SQL. Cependant, il n'a pas :). DevProducts est mon testtable avec une colonne Product and Attribute. Je pensais que je posterais la requête LINQ de toute façon, pourrait être utile aux gens qui jouent avec LINQ.

Si vous pouvez optimiser la requête LINQ ci-dessus, s'il vous plaît laissez-moi savoir (il pourrait conduire à une meilleure SQL;))

+0

Je peux le faire fonctionner dans LINQ, mais le problème est que LINQ est plus lent que de simplement le produire dans une procédure stockée ou quelque chose avec un chemin d'exécution. Si je devais faire quelque chose comme ça, j'utiliserais une requête compilée pour l'accélérer. – Praesidium

0

J'utilise DDL suivante

CREATE TABLE Attributes (
    Asset  VARCHAR(100) 
    , Name  VARCHAR(100) 
    , UNIQUE(Asset, Name) 
    ) 

deuxième question est facile

SELECT Name 
FROM  Attributes 
WHERE Name IN (SELECT Name FROM Attributes WHERE Asset = 'A') 
     AND Asset = 'B' 

première question n'est pas plus difficile

SELECT Asset 
FROM  Attributes 
WHERE Name IN (SELECT Name FROM Attributes WHERE Asset = 'A') 
GROUP BY Asset 
HAVING COUNT(*) = (SELECT COUNT(*) FROM FROM Attributes WHERE Asset = 'A') 

Edit:

Je suis parti AND Asset != 'A' de la clause WHERE du second extrait par souci de concision