【问题标题】:SQL: How to generate non-existing combinationsSQL:如何生成不存在的组合
【发布时间】:2018-10-23 17:08:02
【问题描述】:

我想通过两个词的组合创建一个巨大的列表。字典中的每个单词都应该与所有其他单词结合。换句话说,我将拥有Total^2 组合。每个单词都有一个不同的 ID,我只在组合表中生成两个 id 的组合。

我想定期检查是否遗漏了任何组合,以便生成并将其添加到数据库中。我发现this Q/A 可以生成所有可能的组合,但我不知道如何使用 SQL 查询找到不存在的组合,可能是这样的:

select * from words a ... join words b 
where (a.id, b.id) not in (select * from combinaions) 

如果 SQL 对此没有直接的解决方案,请您建议一种算法来以编程方式执行此操作。请注意,可能缺少一些 ID,因为我删除了一些单词,所以我不能对整数使用线性循环。

combinations表有两列(第一个id,第二个id),两个id都来自表words

【问题讨论】:

  • 您的数据结构如何?
  • 我编辑了问题:组合表有两列(第一个 id,第二个 id)两个 id 都来自表词@Zack
  • 您的“类似这样”的建议可能并不遥远。可能包括not in 选择查询中的特定列,所以where (a.id, b.id) not in (select id1, id2 from combinations)。发布您的表结构会有所帮助。

标签: sql sql-server


【解决方案1】:

您可以使用交叉连接来获得所有可能的组合,然后根据条件,您可以删除已经存在的组合。

Select * from words a cross join words b 
where not exists (select * from combinations c where c.first_id = a.id and c.second_id = b.id) 

【讨论】:

    【解决方案2】:

    正如@VahiD 所说,CROSS JOIN 是拼图的核心部分。除了使用子查询,您还可以将现有的组合表 LEFT JOIN 替换为单词的 CROSS JOIN,并检查 NULLs(这意味着给定的笛卡尔积组合在您现有的组合表中不存在) )。

    例如:

    WITH 
        -- sample data (notice that there's no word with ID of 3)
        words(word_id, word) AS
        (
            SELECT 1, 'apple'   UNION ALL
            SELECT 2, 'pear'    UNION ALL
            SELECT 4, 'orange'  UNION ALL
            SELECT 5, 'banana'
        )
        -- existing combinations
        ,combinations(first_id, second_id) AS
        (
            SELECT 1, 2 UNION ALL
            SELECT 1, 5 UNION ALL
            SELECT 2, 4 UNION ALL
            SELECT 2, 5 UNION ALL
            SELECT 4, 5
        )
        -- this is the CTE you'll use to create the cartesian product
        -- of all words in your words table. You can also put this as a 
        -- sub-query, but I'd argue that a CTE makes it clearer.
        ,cartesian(w1_id, w1_word, w2_id, w2_word) AS
        (
            SELECT *
            FROM words w1, words w2
        )
    -- the actual query
    SELECT * 
    FROM cartesian
        LEFT JOIN combinations ON
            combinations.first_id = cartesian.w1_id
            AND combinations.second_id = cartesian.w2_id
    WHERE combinations.first_id IS NULL
    

    现在,一个重要的警告是,当word1word2 切换时,此查询不会将组合视为相同。即(1,2)(2,1) 不同。然而,解决这个问题就像调整你的连接一样简单:

    SELECT * 
    FROM cartesian
        LEFT JOIN combinations ON
            (combinations.first_id = cartesian.w1_id OR combinations.first_id = cartesian.w2_id)
            AND
            (combinations.second_id = cartesian.w1_id OR combinations.second_id = cartesian.w2_id)
    WHERE combinations.first_id IS NULL
    

    【讨论】:

      【解决方案3】:

      这是另一种选择。在子查询中构建完整列表,并将其留在组合表的外部以查找缺少的内容。

      DECLARE @Words TABLE
          (
              [Id] INT
            , [Word] NVARCHAR(200)
          );
      
      DECLARE @WordCombo TABLE
          (
              [Id1] INT
            , [Id2] INT
          );
      
      INSERT INTO @Words (
                             [Id]
                           , [Word]
                         )
      VALUES ( 1, N'Cat' )
           , ( 2, N'Taco' )
           , ( 3, N'Test' )
           , ( 4, N'Cake' )
           , ( 5, N'Apple' )
           , ( 6, N'Pear' );
      
      INSERT INTO @WordCombo (
                                 [Id1]
                               , [Id2]
                             )
      VALUES ( 1, 2 )
           , ( 2, 6 )
           , ( 5, 3 )
           , ( 5, 1 );
      
      --select from a sub query that builds out all combinations and then left outer to find what's missing in @WordCombo
      SELECT          [fulllist].[Id1]
                    , [fulllist].[Id2]
      FROM            (
                          --Rebuild full list
                          SELECT     [a].[Id] AS [Id1]
                                   , [b].[Id] AS [Id2]
                          FROM       @Words [a]
                          INNER JOIN @Words [b]
                              ON 1 = 1
                          WHERE      [a].[Id] <> [b].[Id] --Would a word be combined with itself?
      
                      ) AS [fulllist]
      LEFT OUTER JOIN @WordCombo [wc]
          ON [wc].[Id1] = [fulllist].[Id1]
             AND [wc].[Id2] = [fulllist].[Id2]
      WHERE           [wc].[Id1] IS NULL;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-10
        • 1970-01-01
        • 1970-01-01
        • 2012-11-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多