2009-07-21 12 views
5

Estoy tratando de resolver el siguiente problema.SQL Elaborate Joins Query

Siento que es posible, pero parece que no puedo conseguirlo.

Este es el escenario:

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 yo estoy buscando algo que tiene los mismos atributos que Activos-A, entonces se debe identificar de Activos-B Dado que el activo-B tiene todos los mismos atributos que Activos-A (debe descartarse, ya que el Asset-A no especificó nada diferente o similar). Además, si quisiera los atributos solo para el Asset-A y el Asset-B que eran comunes, ¿cómo lo conseguiría?

parece simple, pero no puede clavarlo ...

La tabla real que estoy usando, es casi exactamente la Tabla2, simplemente una asociación de un assetId, y un AttributeId modo: PK: Id
int: assetId
int: attributeId

que sólo incluía la idea de la mesa de activos para simplificar la cuestión.

+3

Por qué el -1? Pregunta SQL perfectamente válida. +1 –

+0

Cualquier pregunta que parezca "No sé cómo funciona la unión" es muy útil y no tiene un uso más allá del póster original. –

+0

@Mark: esta pregunta es * mucho * más allá de "cómo funciona la unión". – Quassnoi

Respuesta

0

Esta solución funciona como se indica, gracias por la entrada.

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

Entonces, ¿tiene Atributo de tabla con atributo de columna? ¿Tiene NULLs en Attribute, porque de lo contrario, COUNT (ta2. [Attribute]) no es diferente de COUNT (*), excepto que es más lento. El segundo JOIN parece redundante. Aún así, podría hacer el trabajo :-)) – wqw

+0

¡Bueno, estos no son los nombres de las tablas o los nombres de las columnas, sino simplemente representaciones rápidas de lo que estoy tratando de hacer! – 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 

, o en SQL Server 2005 (con datos de muestra para comprobar):

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

Parece que no puedo hacer que funcione, parece que no puedo enlazar nada. Cualquier cosa ... (El identificador de varias partes "ata.AttributeId" no se pudo enlazar). – Praesidium

+0

@Praesidium: ver la actualización de la publicación. – Quassnoi

0

que no entienden completamente la primera parte de su pregunta, la identificación de los activos en función de sus atributos.

hacer algunas suposiciones acerca de los nombres de columna, la siguiente consulta produciría los atributos comunes entre activos-A y del activo-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

Debería estar donde No Existe, pero también devuelve activos que no tienen atributos ... – Praesidium

+0

O activos que tienen solo un atributo en común ... Entonces devolvería Asset-C porque es difícil, aunque sea no azul, como 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

Esto no parece devolver nada ... Todos los activos están descalificados. – Praesidium

+0

Tal vez sea porque utilicé "atributos" (en lugar de "atributo") para el nombre de la tabla en la consulta interna. Corregido Pero, por supuesto, necesita usar los nombres reales de su tabla; No sé lo que son. –

0

Encuentra todos los activos que tienen todos los atributos que " A "tiene (pero también puede tener atributos adicionales):

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

Es decir," encuentre cualquier activo donde no haya ningún atributo de A que no es también un atributo de este activo ".

Encontrar todos los activos que tienen exactamente los mismos atributos como "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 
    ) 
    ) 

Es decir, "encontrar cualquier activo, donde no hay ningún atributo de A que no sea también un atributo de este activo, y donde no hay ningún atributo de este activo que tampoco sea un atributo de A. "

0

Encontrar todos los activos que tienen todos los mismos atributos que el activo-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

pensé que tal vez pueda hacer esto con LINQ y luego trabajar mi camino hacia atrás con:

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

Pensé que traducir esto a SQL con LinqPad resultaría en una consulta SQL bastante. Sin embargo, no :) DevProducts es mi tabla de prueba con una columna Producto y atributo. Pensé que publicaría la consulta LINQ de todos modos, podría ser útil para las personas que están jugando con LINQ.

Si se puede optimizar la consulta LINQ arriba, por favor hágamelo saber (que podría resultar en una mejor SQL;))

+0

Puedo hacerlo funcionar en LINQ, pero el problema es que LINQ es más lento que simplemente producir esto en un procedimiento almacenado o algo con una ruta de ejecución. Si iba a hacer algo así, usaría una consulta compilada para acelerarlo. – Praesidium

0

estoy usando DDL siguiente

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

segunda pregunta es fácil

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

primera pregunta no es más difícil

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

Editar:

Salí AND Asset != 'A' fuera de la WHERE cláusula del segundo fragmento de brevedad