【问题标题】:joining one table multiple times to other tables将一个表多次连接到其他表
【发布时间】:2009-06-10 10:31:21
【问题描述】:

我有三张桌子:

表用户(userid用户名)

表键(用户id keyid)

台式笔记本电脑(用户 ID 笔记本电脑 ID)

我希望所有用户拥有钥匙或笔记本电脑,或两者兼有。我如何编写查询,以便它使用表用户和表键之间的连接,以及表用户和表笔记本电脑之间的连接?

主要问题是在实际场景中,有十二个左右的表连接,比如:

" select .. From a left join b on (...), c join d on (..),e,f,g where ...",

我看到a可以连接到b,a也可以连接到f。所以假设我不能让表 a、b 和 f 并排出现,我该如何编写 sql 查询?

【问题讨论】:

  • 每个用户都必须有钥匙或笔记本电脑。对吗?
  • 你可能是对的,也可能是错的,但完全不在乎!不过还是很有趣:)
  • Q1:你想只知道用户(userid)还是他们到底有什么? Q2:用户可以拥有多于一个类型的物品(2 台笔记本电脑或 3 把钥匙)吗?

标签: sql


【解决方案1】:

您可以使用多个连接来组合多个表:

select *
from user u
left join key k on u.userid = k.userid
left join laptop l on l.userid = u.userid

“左连接”还可以查找没有钥匙或笔记本电脑的用户。如果您将两者都替换为“inner join”,它只会找到拥有笔记本电脑和密钥的用户。

当“左连接”未找到行时,它将在其字段中返回 NULL。因此,您可以选择所有拥有笔记本电脑或密钥的用户,如下所示:

select *
from user u
left join key k on u.userid = k.userid
left join laptop l on l.userid = u.userid
where k.userid is not null or l.userid is not null

NULL 是特殊的,因为您将其比较为“字段不为空”而不是“字段 空”。

在您的评论后添加:假设您有一个与笔记本电脑相关的鼠标,但与用户无关。你可以像这样加入:

select *
from user u
left join laptop l on l.userid = u.userid
left join mouse m on m.laptopid = l.laptopid

如果这不能回答您的问题,您需要进一步澄清。

【讨论】:

  • 我觉得最后一个u.userid应该是k.userid
  • 很抱歉之前没有澄清问题:我已经在问题中添加了更多细节,因此您的回答似乎无法解决我的问题
【解决方案2】:
select distinct u.userid, u.username
from User u 
    left outer join Key     /* k on u.userid = k.userid */
    left outer join Laptop  /* l on u.userid = l.userid */
where k.userid is not null or l.userid is not null

编辑 “主要问题是在实际场景中,有十二个左右的表连接,比如: " select .. From a left join b on (...), c join d on (..),e,f,g where ...", 我看到a可以连接到b,a也可以连接到f。所以假设我不能让表 a、b 和 f 并排出现,我该如何编写 sql 查询?”

您可以根据需要拥有任意数量的左外连接。将具有主键的表连接到其余表或在一个表的字段值应与其他表的字段值匹配的任何其他字段上。

比文字更能说明问题

select * 
from a
 left outer join b on a.pk = b.fk -- a pk should match b fk
 left outer join c on a.pk = c.fk -- a pk should match c fk
 left outer join d on c.pk = d.fk -- c pk should match d fk

等等

【讨论】:

  • 我完成了这个问题,以表明我的主要困难是无法让三个表并排出现,出现全表连接(我的意思是:“来自 c,d”等)夹杂在中间
  • 您仍然会在第一个答案中错过诸如“其中 k.userid 不为空或 l.userid 不为空”之类的 where 子句,以过滤掉没有任何内容的用户。
【解决方案3】:
-- // Assuming that a user can have at max 1 items of each type
SELECT      u.*
-- // Assuming that a user can have more then 1 items of each type, just add DISTINCT:
-- // SELECT      DISTINCT u.*
FROM        "User" u
LEFT JOIN   "Key"    u1 ON u.UserID = u1.UserID
LEFT JOIN   "Laptop" u2 ON u.UserID = u2.UserID
LEFT JOIN   "Server" u3 ON u.UserID = u3.UserID
-- // ...
WHERE       COALESCE(u1.UserID, u2.UserID, u3.UserID /*,...*/) IS NOT NULL

【讨论】:

    【解决方案4】:

    正如您所描述的那样,您只想知道某人是否有笔记本电脑或钥匙。我会用子查询而不是连接来编写查询:

    select * 
    from user 
    where userid in (select userid from key union select userid from laptop)
    

    这样做的原因是,通过加入一个拥有多台笔记本电脑或多个密钥的人将被多次列出(除非您使用distinct)。即使你使用distinct,你最终也会得到一个效率较低的查询(至少在 Oracle 上,查询优化器似乎无法创建一个有效的计划)。

    [编辑以纠正 Rashmi Pandit 指出的内容。]

    【讨论】:

    • 如果指定左外连接,则不会是交叉连接。左外连接比子查询快。
    • 我在 Oracle 数据库中快速尝试了一下:你是对的,它不会导致交叉连接。但它也不比子查询方法快——实际上慢了两倍多。
    • 我想对查询做很少的修改,除了添加更多的 JOIN 和 ON 子句,所以现在我正在寻找一种不使用子查询的方法。我也有 mysql 数据库,其行为可能与嵌套查询中的 oracles 行为不同
    • 我不太确定是否是 Oracle,但我一般听说连接比子查询要快……也许对于大量数据
    • 这非常有帮助,作为一个 SQL n00b,这正是我想要的,而且它在启动时非常易读。
    【解决方案5】:

    解决方案一:

    SELECT * FROM [User] u
    INNER JOIN [Key] k
    ON u.userid = k.userid
    
    UNION
    
    SELECT * FROM [User] u
    INNER JOIN Laptop l
    ON u.userid = l.userid
    
    [...]
    

    解决方案二:

    SELECT * FROM [User] u
    LEFT JOIN [Key] k
    ON u.userid = k.userid
    LEFT JOIN Laptop l
    ON u.userid = l.userid
    LEFT JOIN [...]
    WHERE k.userid IS NOT NULL
    OR l.userid IS NOT NULL
    OR [...]
    

    只是一个猜测,你也可以检查这两个的执行计划,看看 UNION 是否更重,反之亦然。

    【讨论】:

      【解决方案6】:
       SELECT * 
       FROM User
       LEFT JOIN Key ON User.id = Key.user_id
       LEFT JOIN Laptop ON User.id = Laptop.user_id
       WHERE Key.id IS NOT NULL OR Laptop.id IS NOT NULL
      

      【讨论】:

        猜你喜欢
        • 2016-02-17
        • 1970-01-01
        • 1970-01-01
        • 2018-09-01
        • 2023-03-18
        • 1970-01-01
        • 1970-01-01
        • 2015-01-25
        • 1970-01-01
        相关资源
        最近更新 更多