MySQL内部联接返回同一行的倍数

Posted

技术标签:

【中文标题】MySQL内部联接返回同一行的倍数【英文标题】:MySQL Inner Join Returning Multiples of the Same Row 【发布时间】:2014-08-20 06:43:38 【问题描述】:

我有两个 mysql 表如下:

resource
-----------------------------------------------
id   name           group   owner_id
-----------------------------------------------
1    MyResource1    hs      11
2    MyResource2    ms      24
3    MyResource3    ps      11
...

resource_access
-----------------------------------------------
id   resource_id    user_id
-----------------------------------------------
1    1              12
2    2              24
3    2              11
4    3              15
...

现在,第一个表当然是资源列表,以及owner_id 列中的各自所有者。第二个表是与另一个用户“共享”该资源的结果。表resource_access可能包含带有user_id 的记录,这与owner_idresource_access 的一行中的owner_id 相同,这是来自所有者交换的混乱清理的结果。

我只想获取用户有权访问的任何资源的 ID、名称和组,无论他们是所有者还是已与他们共享。这是我对示例用户 (24) 的 MySQL 查询:

SELECT resource.id, resource.name, resource.group 
FROM `resource` 
INNER JOIN resource_access ON (
    resource.owner_id='24' 
    OR (
        resource_access.user_id='24' AND 
        resource_access.resource_id=resource.id
    )
)

现在,它会多次返回资源号 2 的 idnamegroup(例如 12 个)。有没有可能的原因?我已经尝试过 LEFTRIGHT 加入并得到相同的结果。 resource 表中有很多记录,但没有一个与 id 为 2 的记录。resource_access 中没有重复的行与同一用户共享资源两次。

提前致谢。

【问题讨论】:

使用SELECT DISTINCT 删除重复项。否则,每个与他们共享的用户都会得到一行,因为JOIN 在两个表之间执行了交叉乘积。 【参考方案1】:

用途:

SELECT DISTINCT resource.id, resource.name, resource.group

删除重复项。

从概念上讲,内连接的工作方式是在两个表之间产生一个完全的叉积。此叉积包含输入表中每 pair 行的一行。然后它保留匹配所有ONWHERE 条件的行,并将其作为结果集返回。如果两个表之间有多个匹配的行,您将在结果集中获得多个行。

如果您从两个表中选择列,您会发现它们实际上不是同一行。它们只是具有来自resource 表的相同数据,但来自resource_access 表的数据不同。但是您没有在结果中显示后面的那些列。使用DISTINCT 会合并结果中的所有这些行。

【讨论】:

是的,我在上面的评论中说了原因。 假设您还在 SELECT 子句中从 resource_access 返回了数据。然后你会看到这些行不是重复的,因为它们在这些其他列中是不同的。您只是没有返回这些列,因此您看不到它们是不同的行。【参考方案2】:

因为您只从resource 表中进行选择,我建议将条件放在where 子句中,而不是使用显式join

SELECT r.id, r.name, r.group 
FROM `resource` r
WHERE r.owner_id='24' or
      EXISTS (select 1
              from resource_access ra
              where ra.resource_id = r.id and
                    ra.user_id = '24' 
             );

按照这个逻辑,“加入”不能产生重复。

【讨论】:

你有一个额外的ON 行。 @Barmar 。 . .谢谢。【参考方案3】:

选择资源的所有权,然后将其与具有访问权限的资源联合。 生成的 user_id 列与您的 WHERE RA.user_id value 不同,这意味着资源是共享给他们的,而不是他们拥有资源。希望这会有所帮助。

SELECT resource.name,resource.group,resource.owner_id AS user_id
  FROM resource
  WHERE resource.owner_id = '11'

UNION

SELECT R.name,R.group,R.owner_id AS user_id
  FROM resource_access RA 
  LEFT JOIN resource R 
     ON (R.id=RA.resource_id)
  WHERE RA.user_id = '11';

【讨论】:

以上是关于MySQL内部联接返回同一行的倍数的主要内容,如果未能解决你的问题,请参考以下文章

缓慢的 MySQL“内部联接”

谁能告诉我,内部联接在 SQL 中是如何工作的?

MySQL 使用 MAX 日期更新内部联接

SQL Server-交叉联接内部联接基础回顾

MySQL:内部联接与 Where [重复]

MYSQL:三个表的内部联接