【问题标题】:SQL Elaborate Joins QuerySQL 详细连接查询
【发布时间】:2010-11-12 16:37:47
【问题描述】:

我正在尝试解决以下问题。

我觉得这是可能的,但我似乎无法得到它。

这是场景:

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

如果我正在寻找与 Asset-A 具有相同属性的东西,那么它应该识别 Asset-B,因为 Asset-B 具有与 Asset-A 相同的属性(它应该丢弃重,因为 Asset-A 没有' t 指定任何不同或类似的内容)。另外,如果我只想要常见的资产 A 和资产 B 的属性,我将如何获得呢?

看起来很简单,但我看不懂……

我使用的实际表几乎就是 Table2,只是 AssetId 和 AttributeId 的关联,所以: PK:身份证
整数:资产 ID
int: AttributeId

我只包含资产表的想法是为了简化问题。

【问题讨论】:

  • 为什么是-1?完全有效的 SQL 问题。 +1
  • 任何看起来像“我不知道连接是如何工作的”的问题都是非常自私的,并且在原始海报之外没有任何用途。
  • @Mark:这个问题远远超出了“连接如何工作”。
  • 我把-1去掉了,但是帖子的标题绝对应该改写。类似于...如何在 SQL 中找到具有相似关系的行?
  • @Mark LOL...是的,我希望我知道 joins 是如何工作的...虽然它绝对是自助服务,但这个问题适用于很多地方...

标签: sql tsql common-table-expression


【解决方案1】:
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

【讨论】:

  • 我似乎无法让它工作,我似乎无法绑定 ata.Anything...(无法绑定多部分标识符“ata.AttributeId”。)
【解决方案2】:

我不完全理解您问题的第一部分,即根据属性识别资产。

对列名进行一些假设,以下查询将产生 Asset-A 和 Asset-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

【讨论】:

    【解决方案3】:
     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'
    

    【讨论】:

    • 应该是不存在的地方,但它也会返回没有属性的资产...
    • 或者只有一个共同属性的资产......所以它会返回 Asset-C,因为它很难,即使它不是蓝色的,比如 Asset-A
    【解决方案4】:
    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");
    

    【讨论】:

    • 这似乎没有返回任何东西......所有资产都被取消资格。
    • 可能是因为我在内部查询中使用了“属性”(而不是“属性”)作为表名。已更正。但是当然你需要使用你的实际表名;我不知道它们是什么。
    【解决方案5】:

    查找具有“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 的属性。”

    【讨论】:

      【解决方案6】:

      此解决方案按规定工作,感谢您的意见。

      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]
      

      【讨论】:

      • 所以你有表属性和列属性? Attribute 中是否有 NULL,否则 COUNT(ta2.[Attribute]) 与 COUNT(*) 没有什么不同,只是速度较慢。第二个 JOIN 似乎是多余的。不过,可能会完成工作:-))
      • 嗯,这些不是实际的表名或列名,只是我想要做的快速表示!
      【解决方案7】:

      查找与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)
      

      【讨论】:

        【解决方案8】:

        我想也许我可以用 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()
        

        我认为使用 LinqPad 将其转换回 SQL 会产生一个漂亮的 SQL 查询。然而它没有:)。 DevProducts 是我的测试表,其中包含 Product 和 Attribute 列。我想我还是会发布 LINQ 查询,可能对正在使用 LINQ 的人有用。

        如果您可以优化上面的 LINQ 查询,请告诉我(它可能会产生更好的 SQL ;))

        【讨论】:

        • 我可以让它在 LINQ 中工作,但问题是 LINQ 比简单地在存储过程或具有执行路径的东西中生成它要慢。如果我要做这样的事情,我会使用编译查询来加快速度。
        【解决方案9】:

        我正在使用以下 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')
        

        编辑:

        为了简洁,我将 AND Asset != 'A' 排除在第二个 sn-p 的 WHERE 子句之外

        【讨论】:

          猜你喜欢
          • 2010-09-22
          • 2018-10-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多