SQL-разработанный запрос на присоединение - PullRequest
5 голосов
/ 21 июля 2009

Я пытаюсь решить следующую проблему.

Я чувствую, что это возможно, но я не могу этого понять.

Вот сценарий:

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

Если я ищу что-то, имеющее те же атрибуты, что и для Актива-A, то оно должно идентифицировать Актив-B, поскольку Актив-B имеет все те же атрибуты, что и Актив-A (он должен отбрасывать тяжелые, так как Актив-A не сделал ' не указывайте ничего другого или похожего). Кроме того, если бы я хотел, чтобы атрибуты только для Актива-А И Актива-B были общими, как бы я это получил?

Кажется простым, но я не могу это прибить ...

Фактическая таблица, которую я использую, это почти точно Table2, просто ассоциация AssetId и AttributeId так: ПК: Id
int: AssetId
int: AttributeId

Я включил идею таблицы активов только для упрощения вопроса.

Ответы [ 9 ]

4 голосов
/ 21 июля 2009
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

или SQL Server 2005 (с данными выборки для проверки):

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 голосов
/ 23 июля 2009

Я использую следующие DDL

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

Второй вопрос легко

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

Первый вопрос не сложнее

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:

Я оставил AND Asset != 'A' из условия WHERE второго фрагмента для краткости

0 голосов
/ 22 июля 2009

Я подумал, что, может быть, я смогу сделать это с помощью LINQ, а затем вернусь назад:

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

Я думал, что перевод этого обратно в SQL с LinqPad приведет к хорошему запросу SQL. Однако это не так :). DevProducts - мой тестовый стол с колонкой Product and Attribute. Я решил в любом случае опубликовать запрос LINQ, он может быть полезен для людей, которые играют с LINQ.

Если вы можете оптимизировать запрос LINQ выше, пожалуйста, дайте мне знать (это может привести к улучшению SQL;))

0 голосов
/ 21 июля 2009

Найти все активы, которые имеют все те же атрибуты, что и asset-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 голосов
/ 21 июля 2009

Это решение работает в соответствии с предписаниями, спасибо за ввод.

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 голосов
/ 21 июля 2009

Найти все активы, которые имеют каждый атрибут, который имеет «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
      )
    ) 

Т.е., "найти любой актив, для которого нет атрибута A, который также не является атрибутом этого актива".

Найти все активы, которые имеют точно такие же атрибуты, что и "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
      )
   )

т.е., "найти любой актив, для которого нет атрибута A, который не является также атрибутом этого актива, и где нет атрибута этого актива, который также не является атрибутом A".

0 голосов
/ 21 июля 2009
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 голосов
/ 21 июля 2009
 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 голосов
/ 21 июля 2009

Я не совсем понимаю первую часть вашего вопроса, определяя активы на основе их атрибутов.

Если сделать некоторые предположения относительно имен столбцов, следующий запрос выведет общие атрибуты между Активом A и Активом 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
...