2009-07-21 4 views
5

Ich versuche, das folgende Problem zu lösen.SQL Elaborate Joins Abfrage

Ich fühle mich wie es möglich ist, aber ich kann nicht scheinen, es zu bekommen.

Hier ist das Szenario:

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 

Wenn ich nach etwas suchen die gleichen Attribute wie Asset A, dann sollte es Anlagegut-B seit Vermögens-B hat die gleichen Attribute wie Asset A identifizieren (Es sollte schwer verworfen werden, da Asset-A nichts anderes oder Ähnliches spezifiziert hat). Außerdem, wenn ich die Attribute nur für Asset-A UND Asset-B wollte, die üblich waren, wie würde ich das bekommen?

Scheint einfach, aber ich kann es nicht nageln ...

Die eigentliche Tabelle I, bin mit fast genau Table2, einfach eine Vereinigung eines AssetID und ein AttributeId so: PK: ID
int: AssetID
int: attributeID

ich nur die Idee des Asset-Tabelle enthalten, die Frage zu vereinfachen.

+3

Warum die -1 AND Asset != 'A' aus der WHERE Klausel des zweiten Schnipsel übrig? Perfekt gültige SQL-Frage. +1 –

+0

Jede Frage, die wie "Ich weiß nicht, wie Joins funktioniert" aussieht, ist ziemlich eigennützig und hat keine Verwendung jenseits des ursprünglichen Posters. –

+0

@Mark: Diese Frage ist * weit * darüber hinaus "wie Joins arbeiten". – Quassnoi

Antwort

0

Diese Lösung funktioniert wie vorgeschrieben, danke für die Eingabe.

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

Sie haben also Tabellenattribut mit Spaltenattribut? Haben Sie NULL in Attribut, weil sonst COUNT (ta2. [Attribut]) nicht anders als COUNT (*) ist, außer es ist langsamer. Der zweite JOIN scheint redundant zu sein. Trotzdem, könnte die Arbeit erledigt werden :-)) – wqw

+0

Nun, das sind nicht die tatsächlichen Tabellennamen oder Spaltennamen, nur schnelle Darstellungen von dem, was ich versuche zu tun! – 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 

oder in SQL Server 2005 (mit Beispieldaten zu überprüfen):

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

Ich kann nicht scheinen, um das zu arbeiten, ich kann nicht ata.Anything zu binden scheinen. (Die mehrteilige Kennung "ata.AttributeId" konnte nicht gebunden werden.) – Praesidium

+0

@Praesidium: siehe Post-Update. – Quassnoi

0

Ich verstehe nicht ganz, den ersten Teil Ihrer Frage, Vermögenswerte auf der Grundlage ihrer Attribute zu identifizieren.

einige Annahmen über Spaltennamen zu machen, wird die folgende Abfrage, um die gemeinsamen Eigenschaften zwischen dem Asset-A and Asset-B ergäbe:

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

Es sollte sein, wo nicht existiert, aber es gibt auch Assets zurück, die keine Attribute haben ... – Praesidium

+0

Oder Assets, die nur ein einziges Attribut gemeinsam haben ... Es würde also Asset-C zurückgeben, weil es schwer ist, obwohl es es ist nicht blau, wie 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

Dies scheint nichts zurückzugeben ... Alle Vermögenswerte werden disqualifiziert. – Praesidium

+0

Vielleicht liegt es daran, dass ich "Attribute" (anstelle von "Attribut") für den Tabellennamen in der inneren Abfrage verwendet habe. Korrigiert. Aber natürlich müssen Sie Ihre tatsächlichen Tabellennamen verwenden; Ich weiß nicht, was sie sind. –

0

alle Assets finden, die jedes Attribut haben, dass " A „(aber auch zusätzliche Attribute aufweisen kann):

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“, einen Vermögenswert finden, wo es kein Attribut A, das ist nicht auch ein Attribut dieses Vermögenswerts ".

alle Assets finden, die genau die gleichen Attribute wie „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 „, einen Vermögenswert finden, wo es kein Attribut von A, die nicht auch ein Attribut dieser Vermögenswert ist, und wo Es gibt kein Attribut dieses Assets, das nicht auch ein Attribut von A ist. "

0

alle Assets finden, die alle die gleichen Attribute wie Asset-a haben:

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

Ich dachte, vielleicht kann ich tun dies mit LINQ und dann meinen Weg nach hinten mit:

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

Ich dachte, dass die Übersetzung in SQL mit LinqPad in eine hübsche SQL-Abfrage führen würde. Aber es hat nicht :). DevProducts ist meine Testtabelle mit einer Spalte Produkt und Attribut. Ich dachte, ich würde die LINQ-Abfrage trotzdem posten, könnte nützlich sein für Leute, die mit LINQ herumspielen.

Wenn Sie die LINQ-Abfrage oben optimieren können, lassen Sie es mich wissen (es besser SQL führen könnte;))

+0

Ich kann es in LINQ arbeiten lassen, aber das Problem ist, dass LINQ langsamer ist, als einfach in einer gespeicherten Prozedur oder etwas mit einem Ausführungspfad zu produzieren. Wenn ich so etwas machen würde, würde ich eine Compiled Query verwenden, um es zu beschleunigen. – Praesidium

0

Ich verwende DDL folgenden

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

Zweite Frage ist leicht

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

Erste Frage ist nicht schwieriger

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:

ich der Kürze halber