执行INNER JOIN时重复的结果

时间:2012-11-23 19:44:47

标签: sql-server sql-server-2008 inner-join cartesian-product

我有2个简单的表格,我想用INNER JOIN执行,但问题是我得到重复(对于列str1和str2)结果:

CREATE TABLE #A (Id INT, str1 nvarchar(50), str2 nvarchar(50))
insert into #A  values (1, 'a', 'b')
insert into #A  values (2, 'a', 'b')

CREATE TABLE #B (Id INT, str1 nvarchar(50), str2 nvarchar(50))
insert into #B values (7, 'a', 'b')
insert into #B  values (8, 'a', 'b')

select * from #A a
INNER JOIN #B b ON a.str1 = b.str1 AND a.str2 = b.str2  

当我真正想要2时,它给了我4条记录。

我得到了什么:
id | str1 | STR2 | id | str1 | STR2
1 | a | b | 7 | a | b
2 | a | b | 7 | a | b
1 | a | b | 8 | a | b
2 | a | b | 8 | a | B'/ P>

我真正想要的是什么:
1 a | b | 7 | a | b
2 a | b | 8 | a | B'/ P>

有人可以帮忙吗?我知道这是可以使用游标和循环实现的,但是我想避免它,并且如果可能的话只使用某种类型的JOIN。

3 个答案:

答案 0 :(得分:5)

SELECT 
    a.id AS a_id, a.str1 AS a_str1, a.str2 AS a_str2, 
    b.id AS b_id, b.str1 AS b_str1, b.str2 AS b_str2
FROM 
    ( SELECT *
           , ROW_NUMBER() OVER (PARTITION BY str1, str2 ORDER BY id) AS rn
      FROM #A 
    ) a
  INNER JOIN 
    ( SELECT *
           , ROW_NUMBER() OVER (PARTITION BY str1, str2 ORDER BY id) AS rn
      FROM #B 
    ) b 
    ON  a.str1 = b.str1 
    AND a.str2 = b.str2 
    AND a.rn = b.rn ;

如果同一(str1, str2)组合的一个或另一个表中有更多行,您可以通过将INNER联接更改为LEFT,{{{}来选择要返回的行1}}或RIGHT加入。

答案 1 :(得分:3)

可以完成与以下查询的匹配(SQL 2005及更高版本):

WITH A AS (
   SELECT
      Seq = Row_Number() OVER (PARTITION BY Str1, Str2 ORDER BY Id),
      *
   FROM #A
), B AS (
   SELECT
      Seq = Row_Number() OVER (PARTITION BY Str1, Str2 ORDER BY Id),
      *
   FROM #B
)
SELECT
   A.Id, A.Str1, A.Str2, B.Id, B.Str1, B.Str2
FROM
   A
   FULL JOIN B
      ON A.Seq = B.Seq AND A.Str1 = B.Str1 AND A.Str2 = B.Str2;

这会将A和B之间的项目连接到其ID顺序位置。但请注意:如果每组Str1和Str2的项目数不等,则可能会出现意外结果,因为#A或#B会出现NULL。

我假设你想要第一行表#A的“Str1 Str2”,按#A.Id(1为第一个)排序,与表#B的第一行“Str1 Str2”相关联,按照#B.Id(7为第一个)的顺序排序,依此类推,每个连续编号的行。是吗?

但是如果行数不匹配,你会怎么做?例如,#A中有3行与#B中的2行具有相同的值?或者相反?你想看到什么?

仅仅是DISTINCT不能完成这项工作,因为数据不会重复。您将获得实际上是部分交叉连接(导致部分笛卡尔积)。也就是说,您的连接标准不能确保#A行与#B行一一对应。当发生这种情况时,对于#A中的每个行,您将获得 B中每个匹配行的输出行.2 x 2 = 4,而不是2。

我认为如果你在你的例子中更具体一点,那会有所帮助。你实际在查询什么?当然,你已经为我们简化了,但这也消除了我们知道你在现实世界中想要完成什么的所有背景。如果你试图排队运动队,我们可能会提供一个不同的答案,而不是你试图排队发票行项目或迟到的事件或谁知道什么!

答案 2 :(得分:1)

使用这些数据,只有那些数据,你无法得到你想要的结果,除非你能够为每个#A的ID值提供一些方法来映射到每个#B的ID值。

所以,如果你真的在每个表中只有2条记录,它会是这样的:

SELECT *
FROM   #A a
   JOIN #B b
      ON a.str1 = b.str1  -- actually, if you join by IDs this isn't necessary
      AND a.str2 = b.str2 -- nor is this
      AND 
      (
          ( a.ID = 1 and b.ID = 7 )
       OR ( a.ID = 2 and b.ID = 8 )
      )

您获得的内容称为笛卡尔积,其中#A中的每条记录与#B中的每条匹配记录配对。由于每个表中有多个匹配记录,因此您可以获得A和B中匹配记录的所有可能组合。

由于您必须使用的其他唯一字段是ID字段,因此您需要使用这些字段将一个A记录与一个B记录完全组合。