【问题标题】:T-SQL: How do I get the rows from one table whose values completely match up with values in another table?T-SQL:如何从一个表中获取其值与另一表中的值完全匹配的行?
【发布时间】:2010-09-11 08:32:16
【问题描述】:

鉴于以下情况:

declare @a table
(
    pkid int,
    value int
)

declare @b table
(
    otherID int,
    value int
)


insert into @a values (1, 1000)
insert into @a values (1, 1001)
insert into @a values (2, 1000)
insert into @a values (2, 1001)
insert into @a values (2, 1002)

insert into @b values (-1, 1000)
insert into @b values (-1, 1001)
insert into @b values (-1, 1002)

如何查询@a 中与@b 完全匹配的所有值?

{@a.pkid = 1, @b.otherID = -1} 不会被返回(3 个值中只有 2 个匹配)

{@a.pkid = 2, @b.otherID = -1} 将被返回(3 个值中的 3 个匹配)

可以选择重构表。

编辑:我已经从 James 和 Tom H 的回答中获得了成功。

当我在 @b 中添加另一个案例时,它们有点不足。

insert into @b values (-2, 1000)

假设这应该返回另外两行({@a.pkid = 1, @b.otherID = -2}{@a.pkid = 2, @b.otherID = -2},它不起作用。但是,对于我的项目来说,这不是问题。

【问题讨论】:

  • 我修改了我的以解决重复问题

标签: sql-server tsql


【解决方案1】:

有几种方法,但一种简单的方法是创建一个联合视图

create view qryMyUinion as
select * from table1 
union all
select * from table2

小心使用 union all,而不是简单的 union,因为它会省略重复项

然后这样做

select count( * ), [field list here] 
from qryMyUnion
group by [field list here]
having count( * ) > 1

Union 和 Have 语句往往是标准 SQL 中最容易被忽视的部分,但它们可以解决许多原本需要过程代码的棘手问题

【讨论】:

    【解决方案2】:

    可能不是最便宜的方法:

    SELECT a.pkId,b.otherId FROM
        (SELECT a.pkId,CHECKSUM_AGG(DISTINCT a.value) as 'ValueHash' FROM @a a GROUP BY a.pkId) a
        INNER JOIN (SELECT b.otherId,CHECKSUM_AGG(DISTINCT b.value) as 'ValueHash' FROM @b b GROUP BY b.otherId) b
    ON a.ValueHash = b.ValueHash
    

    您可以看到,基本上我正在为每个表中的每个 Id 的一组值创建一个新结果集,并仅在它们匹配的地方加入。

    【讨论】:

    • 我必须说您的解决方案是一种有趣的方法。我不确定我是否会在不使用其他一些双重检查方法的情况下依赖哈希值。但无论如何,它读起来很有趣,所以我只想说它很酷。
    • 我认为这是一个非常巧妙的解决方案,所以我不得不添加我的投票。
    • 谢谢。是的,如果有大量数据,我会对仅依赖单个哈希值持怀疑态度。通过在每个 a 和 b 值附加一些固定值后进行第二次散列,您可以获得很大的信心,而不会增加一倍的费用。像 CHECKSUM_AGG(a.value + 'aaa')
    • 我收回这一点……我认为这样做几乎会使费用翻倍。
    【解决方案3】:

    如果您试图只返回完整的记录集,您可以试试这个。不过,我肯定会推荐使用有意义的别名...

    Cervo 是对的,我们需要额外检查以确保 a 与 b 完全匹配,而不是 b 的超集。在这一点上,这更像是一个笨拙的解决方案,因此这仅在其他解决方案中的分析功能不起作用的情况下才合理。

    select 
        a.pkid,
        a.value
    from
        @a a
    where
        a.pkid in
        (
        select
            pkid
        from
            (
            select 
                c.pkid,
                c.otherid,
                count(*) matching_count
            from 
                (
                select 
                    a.pkid,
                    a.value,
                    b.otherid
                from 
                    @a a inner join @b b 
                    on a.value = b.value
                ) c
            group by 
                c.pkid,
                c.otherid
            ) d
            inner join
            (
            select 
                b.otherid,
                count(*) b_record_count
            from
                @b b
            group by
                b.otherid
            ) e
            on d.otherid = e.otherid
            and d.matching_count = e.b_record_count
            inner join
            (
            select 
                a.pkid match_pkid,
                count(*) a_record_count
            from
                @a a
            group by
                a.pkid
            ) f
            on d.pkid = f.match_pkid
            and d.matching_count = f.a_record_count
        )
    

    【讨论】:

    • 我认为这行不通。我拿了你的代码,用上面的代码剪切并粘贴了它,并将行插入添加到 @a 值 (2, 1003) 中。问题是您也没有检查 @a 中的计数。因此,如果 B 与 A 中的所有内容匹配,则您说它是正确的。这会忽略 A 的条目比 b 多。
    【解决方案4】:

    适用于您的示例,我认为它适用于所有情况,但我尚未对其进行彻底测试:

    SELECT
        SQ1.pkid
    FROM
        (
            SELECT
                a.pkid, COUNT(*) AS cnt
            FROM
                @a AS a
            GROUP BY
                a.pkid
        ) SQ1
    INNER JOIN
        (
            SELECT
                a1.pkid, b1.otherID, COUNT(*) AS cnt
            FROM
                @a AS a1
            INNER JOIN @b AS b1 ON b1.value = a1.value
            GROUP BY
                a1.pkid, b1.otherID
        ) SQ2 ON
            SQ2.pkid = SQ1.pkid AND
            SQ2.cnt = SQ1.cnt
    INNER JOIN
        (
            SELECT
                b2.otherID, COUNT(*) AS cnt
            FROM
                @b AS b2
            GROUP BY
                b2.otherID
        ) SQ3 ON
            SQ3.otherID = SQ2.otherID AND
            SQ3.cnt = SQ1.cnt
    

    【讨论】:

      【解决方案5】:

      正如 CQ 所说,您只需要一个简单的内部联接。

      Select * -- all columns but only from #a
      from #a 
      inner join #b 
      on #a.value = #b.value -- only return matching rows
      where #a.pkid  = 2
      

      【讨论】:

        【解决方案6】:
        -- 注意,只有在任一表中不允许重复值的情况下才有效 声明@validcomparisons 表( pkid INT, 其他 INT, 整数 ) 插入@validcomparisons(pkid,otherid,num) 选择 a.pkid、b.otherid、A.cnt FROM (select pkid, count(*) as cnt FROM @a group by pkid) a INNER JOIN (select otherid, count(*) as cnt from @b group by otherid) b ON b.cnt = a.cnt 声明@比较表 ( pkid INT, 其他 INT, 相同的INT) 插入@comparison(pkid,otherid,相同) 选择 a.pkid,b.otherid,计数(*) 来自@a 内连接@b b ON a.value = b.value 按 a.pkid、b.otherid 分组 选择 COMP.PKID、COMP.OTHERID 来自@comparison comp 内部联接@validcomparisons val ON comp.pkid = val.pkid AND comp.otherid = val.otherid AND comp.same = val.num

        【讨论】:

          【解决方案7】:

          以下查询为您提供请求的结果:

          select A.pkid, B.otherId
              from @a A, @b B 
              where A.value = B.value
              group by A.pkid, B.otherId
              having count(B.value) = (
                  select count(*) from @b BB where B.otherId = BB.otherId)
          

          【讨论】:

            【解决方案8】:

            进一步迭代该点:

            select a.*
            from @a a 
            inner join @b b on a.value = b.value
            

            这将返回@a 中与@b 匹配的所有值

            【讨论】:

              【解决方案9】:

              我添加了一些额外的测试用例。您可以通过更改在聚合中使用不同关键字的方式来更改重复处理。基本上,我得到了匹配计数并将其与每个 @a 和 @b 中所需匹配的计数进行比较。

              declare @a table
              (
                  pkid int,
                  value int
              )
              
              declare @b table
              (
                  otherID int,
                  value int
              )
              
              
              insert into @a values (1, 1000)
              insert into @a values (1, 1001)
              
              insert into @a values (2, 1000)
              insert into @a values (2, 1001)
              insert into @a values (2, 1002)
              
              insert into @a values (3, 1000)
              insert into @a values (3, 1001)
              insert into @a values (3, 1001)
              
              insert into @a values (4, 1000)
              insert into @a values (4, 1000)
              insert into @a values (4, 1001)
              
              
              insert into @b values (-1, 1000)
              insert into @b values (-1, 1001)
              insert into @b values (-1, 1002)
              
              insert into @b values (-2, 1001)
              insert into @b values (-2, 1002)
              
              insert into @b values (-3, 1000)
              insert into @b values (-3, 1001)
              insert into @b values (-3, 1001)
              
              
              
              SELECT Matches.pkid, Matches.otherId
              FROM
              (
                  SELECT a.pkid, b.otherId, n = COUNT(*)
                  FROM @a a
                  INNER JOIN @b b
                      ON a.Value = b.Value
                  GROUP BY a.pkid, b.otherId
              ) AS Matches
              
              INNER JOIN 
              (
                  SELECT
                      pkid,
                      n = COUNT(DISTINCT value)
                  FROM @a
                  GROUP BY pkid
              ) AS ACount
              ON Matches.pkid = ACount.pkid
              
              INNER JOIN
              (
                  SELECT
                      otherId,
                      n = COUNT(DISTINCT value)
                  FROM @b
                  GROUP BY otherId
              ) AS BCount
                  ON Matches.otherId = BCount.otherId
              
              WHERE Matches.n = ACount.n AND Matches.n = BCount.n
              

              【讨论】:

                【解决方案10】:

                1) 我假设你没有重复的 id

                2) 获取相同数量值的key

                3)key值个数等于等值个数的行为目标

                我希望这是您搜索的内容(您不搜索性能,不是吗?)

                declare @a table(    pkid int,    value int)
                declare @b table(    otherID int,    value int)
                
                insert into @a values (1, 1000)
                insert into @a values (1, 1001)
                insert into @a values (2, 1000)
                insert into @a values (2, 1001)
                insert into @a values (2, 1002)
                insert into @a values (3, 1000)  
                insert into @a values (3, 1001)
                insert into @a values (4, 1000)
                insert into @a values (4, 1001)
                insert into @b values (-1, 1000)
                insert into @b values (-1, 1001)
                insert into @b values (-1, 1002)
                insert into @b values (-2, 1001)
                insert into @b values (-2, 1002)
                insert into @b values (-3, 1000)
                insert into @b values (-3, 1001)
                
                  select cntok.cntid1 as cntid1, cntok.cntid2 as cntid2
                  from
                 (select cnt.cnt, cnt.cntid1, cnt.cntid2 from
                    (select acnt.cnt as cnt, acnt.cntid as cntid1, bcnt.cntid as cntid2 from
                          (select count(pkid) as cnt, pkid as cntid from @a group by pkid)
                           as acnt
                                full join 
                               (select count(otherID) as cnt, otherID as cntid from @b group by otherID)
                                as bcnt
                                   on  acnt.cnt = bcnt.cnt)
                     as cnt
                     where cntid1 is not null and cntid2 is not null)
                   as cntok 
                inner join 
                (select count(1) as cnt, cnta.cntid1 as cntid1, cnta.cntid2 as cntid2
                from
                    (select cnt, cntid1, cntid2, a.value as value1 
                     from
                         (select cnt.cnt, cnt.cntid1, cnt.cntid2 from
                            (select acnt.cnt as cnt, acnt.cntid as cntid1, bcnt.cntid as cntid2 from
                                  (select count(pkid) as cnt, pkid as cntid from @a group by pkid)
                                   as acnt
                                        full join 
                                       (select count(otherID) as cnt, otherID as cntid from @b group by otherID)
                                        as bcnt
                                           on  acnt.cnt = bcnt.cnt)
                             as cnt
                             where cntid1 is not null and cntid2 is not null)
                         as cntok 
                             inner join @a as a on a.pkid = cntok.cntid1)
                      as cnta
                         inner join
                
                             (select cnt, cntid1, cntid2, b.value as value2 
                             from
                             (select cnt.cnt, cnt.cntid1, cnt.cntid2 from
                                    (select acnt.cnt as cnt, acnt.cntid as cntid1, bcnt.cntid as cntid2 from
                                          (select count(pkid) as cnt, pkid as cntid from @a group by pkid)
                                           as acnt
                                                full join 
                                               (select count(otherID) as cnt, otherID as cntid from @b group by otherID)
                                                as bcnt
                                                   on  acnt.cnt = bcnt.cnt)
                                     as cnt
                                     where cntid1 is not null and cntid2 is not null)
                                 as cntok 
                                     inner join @b as b on b.otherid = cntok.cntid2)
                               as cntb
                               on cnta.cntid1 = cntb.cntid1 and cnta.cntid2 = cntb.cntid2 and cnta.value1 = cntb.value2
                      group by cnta.cntid1, cnta.cntid2) 
                   as cntequals
                   on cntok.cnt = cntequals.cnt and cntok.cntid1 = cntequals.cntid1 and cntok.cntid2 = cntequals.cntid2
                

                【讨论】:

                  【解决方案11】:

                  如何查询@a 中与@b 完全匹配的所有值?

                  恐怕这个定义不是很清楚。从您的附加示例看来,您希望所有 a.pkid、b.otherID 对,其中给定 b.otherID 的每个 b.value 也是给定 a.pkid 的 a.value。

                  换句话说,您希望@a 中的pkid 至少具有b 中其他ID 的所有值。 @a 中的额外值似乎没问题。同样,这是基于您的附加示例的推理,以及 (1, -2) 和 (2, -2) 将是有效结果的假设。在这两种情况下,给定 pkid 的 a.value 值大于给定 otherID 的 b.value 值。

                  所以,考虑到这一点:

                      select
                      matches.pkid
                      ,matches.otherID
                  from
                  (
                      select 
                          a.pkid
                          ,b.otherID
                          ,count(1) as cnt
                      from @a a
                      inner join @b b
                          on b.value = a.value
                      group by 
                          a.pkid
                          ,b.otherID
                  ) as matches
                  inner join
                  (
                      select
                          otherID
                          ,count(1) as cnt
                      from @b
                      group by otherID
                  ) as b_counts
                  on b_counts.otherID = matches.otherID
                  where matches.cnt = b_counts.cnt
                  

                  【讨论】:

                    【解决方案12】:

                    这样更高效(它使用TOP 1 而不是COUNT),并且与(-2, 1000) 一起使用:

                    SELECT  *
                    FROM    (
                            SELECT  ab.pkid, ab.otherID,
                                    (
                                    SELECT  TOP 1 COALESCE(ai.value, bi.value)
                                    FROM    (
                                            SELECT  *
                                            FROM    @a aii
                                            WHERE   aii.pkid = ab.pkid
                                            ) ai
                                    FULL OUTER JOIN
                                            (
                                            SELECT  *
                                            FROM    @b bii
                                            WHERE   bii.otherID = ab.otherID
                                            ) bi
                                    ON      ai.value = bi.value
                                    WHERE   ai.pkid IS NULL OR bi.otherID IS NULL
                                    ) unmatch
                            FROM
                                    (
                                    SELECT  DISTINCT pkid, otherid
                                    FROM    @a a , @b b
                                    ) ab
                            ) q
                    WHERE   unmatch IS NOT NULL
                    

                    【讨论】:

                    • 我遇到了类似的问题,并决定使用您的解决方案而不是 James 的解决方案(我认为他的解决方案很好但很巧妙)。为你 +1。
                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2020-11-03
                    • 1970-01-01
                    • 2020-05-09
                    • 1970-01-01
                    • 2018-07-05
                    相关资源
                    最近更新 更多