【问题标题】:LEFT JOIN - narrow things downLEFT JOIN - 缩小范围
【发布时间】:2016-09-02 20:57:57
【问题描述】:

我目前在新工作中继承的旧版应用程序存在问题。我有一个 SQL 查询太长而无法响应,我需要找到一种方法来固定它。 此查询作用于 3 个表:

  • SESSION 包含所有用户访问
  • CONTACT 包含人们通过表单发送的所有消息,并包含一个链接回 SESSION id 字段的“session_id”字段
  • ACCOUNT 包含用户帐户(在网站上注册的人),并且其“id”字段在 SESSION 中链接回(通过“SESSION.account_id”字段)。除了 SESSION 表(旧版应用...)之外,ACCOUNT 和 CONTACT 没有任何关联。

很遗憾,我无法更改此结构。

我的查询尝试恢复所有有趣的会话以提供给管理员。我需要找到链接回帐户或联系表单的所有会话。

目前,查询的结构如下:

SELECT s.id
/*  a few fields from ACCOUNT and CONTACT tables */

FROM session s
LEFT JOIN account act ON act.id = s.account_id
LEFT JOIN contact c on c.session_id = s.id
WHERE s.programme_id = :program_id
AND (
c.id IS NOT NULL
OR
act.id IS NOT NULL
)

问题是,SESSION 表增长得非常快(正如您所料),并且有 40 万条记录,它会减慢某些程序的速度(查询中的 :programme_id)。

我尝试使用带有两个 INNER JOIN 查询的 UNION 查询,一个在 SESSION 和 ACCOUNT 之间,另一个在 SESSION 和 CONTACT 之间,但它没有给我相同数量的记录,我真的不明白为什么.

有人可以帮我找到一个更好的方法来进行这个查询吗?

非常感谢。

【问题讨论】:

    标签: mysql sql left-join


    【解决方案1】:

    我认为你只需要索引。对于这个查询:

    SELECT s.id
    /*  a few fields from ACCOUNT and CONTACT tables */
    
    FROM session s LEFT JOIN
         account act
         ON act.id = s.account_id LEFT JOIN
         contact c 
         ON c.session_id = s.id
    WHERE s.programme_id = :program_id AND
          (c.id IS NOT NULL OR act.id IS NOT NULL);
    

    您希望在session(programme_id, account_id, id)account(id)contact(session_id) 上建立索引。

    重要的是programme_idsession 索引中的第一列。

    【讨论】:

    • 天哪,我是个白痴,非常感谢。我实际上考虑过索引,但我太忙于优化这个查询。我的查询从 30 秒变为 0.02 秒!
    【解决方案2】:

    @Gordon 已经建议您添加索引,这通常是简单有效的解决方案,因此我将回答您问题的不同部分。

    我尝试将 UNION 查询与两个 INNER JOIN 查询一起使用,其中一个在 SESSION 和 ACCOUNT 以及 SESSION 和 CONTACT 之间的另一个,但是 它没有给我相同数量的记录,我真的没有 明白为什么。

    这部分相当简单:JOIN 返回一个结果集,其中包含连接在一起的两个表的行。所以在第一种情况下,你最终会得到一个看起来像

    的结果
    session.id, session.column2, session.column3, ..., account.id, account.column2, account.column3, ....
    

    还有一秒在哪里

    session.id, session.column2, session.column3, ..., contact.id, contact.column2, contact.column3, ....
    

    然后UNION 将失败,除非contactaccount 表具有相同数量的对应类型的列,这是不太可能的。否则,数据库将无法执行UNIONFrom the docs(强调我的):

    第一个 SELECT 语句中的列名用作返回结果的列名。 每个 SELECT 语句的相应位置列出的选定列应具有相同的数据类型。(例如,第一个语句选择的第一列应与其他语句选择的第一列具有相同的类型.)

    如果您不确定,只需分别执行INNER JOINs 并比较结果。

    如果您想坚持使用UNION 解决方案,请确保仅在相应列上执行SELECT:执行SELECT s.id 将是微不足道的,但它应该可以工作,例如。

    【讨论】:

    • 感谢您的信息,非常感谢。在我的测试中,我在每个查询中添加了一些 NULL 字段以匹配字段数,因此可能是这个技巧导致我的查询失败。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-06-09
    • 2019-07-07
    • 2015-07-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多