【问题标题】:Is a left outer join needed when I only need one column from the joined (right) table in the WHERE clause?当我只需要 WHERE 子句中连接(右)表中的一列时,是否需要左外连接?
【发布时间】:2018-12-10 01:08:18
【问题描述】:

我有以下左外连接查询:

SELECT table_left.pk_id, table_left.name
FROM table_left left outer join table_right on table_right.fk_id = table_left.pk_id
WHERE table_right.name like '%entered search value%'

我遇到的问题是 table_right 有超过 1,000,000 行和超过 60 列。该查询大约需要 1 分钟,我认为这是因为它正在对所有列进行完全外部连接。我不需要所有的列。我只需要使用一列(table_right.fk_id),这样我就可以在 WHERE 子句中加入这两个表和另一列(table_right.name)。

我使用外连接是因为我需要在 table_left 中包含 table_right 中没有行的结果。

任何有助于提高上述查询速度的建议将不胜感激。

这是我拥有的两个表的示例:

+-------------------+
| table_left        |
+-------------------+
| pk_id | name      |
+-------+-----------+
| 1     | IBM       |
+-------+-----------+
| 2     | Facebook  |
+-------+-----------+
| 3     | Google    |
+-------+-----------+
| 4     | Microsoft |
+-------+-----------+


+--------------------------------------------+
| table_right                                |
+--------------------------------------------+
| table_right_pk_id | fk_id | job_details    |
+-------------------+-------+----------------+
| 1                 | 1     | Tester         |
+-------------------+-------+----------------+
| 2                 | 2     | Toilet Cleaner |
+-------------------+-------+----------------+
| 3                 | 2     | Secretary      |
+-------------------+-------+----------------+
| 4                 | 3     | Developer      |
+-------------------+-------+----------------+

我希望能够搜索“名称”(在 table_left 中)和“job_details”(在 table_right 中),但使用 table_left 列。这是我提出的查询,查询下方是一些预期结果:

SELECT table_left.pk_id, table_left.name
FROM table_left left outer join table_right on table_right.fk_id = table_left.pk_id
WHERE table_right.name LIKE '%searchTerm%' OR table_left.name LIKE '%searchTerm%'

示例 1

searchTerm = 'IBM'

结果:

+-------------------+
| result            |
+-------------------+
| pk_id | name      |
+-------+-----------+
| 1     | IBM       |
+-------+-----------+

示例 2

searchTerm = '测试者'

结果:

+-------------------+
| result            |
+-------------------+
| pk_id | name      |
+-------+-----------+
| 1     | IBM       |
+-------+-----------+

示例 3

searchTerm = '微软'

结果:(即使 table_right 中没有记录,仍应返回 Microsoft)

+-------------------+
| result            |
+-------------------+
| pk_id | name      |
+-------+-----------+
| 4     | Microsoft |
+-------+-----------+

示例 4

searchTerm = '开发者'

结果:

+-------------------+
| result            |
+-------------------+
| pk_id | name      |
+-------+-----------+
| 2     | Facebook  |
+-------+-----------+

【问题讨论】:

  • 尝试使用explain sql 来检查扫描了多少行以及使用了哪个索引。并且前置% 并不快。无论如何,主要不是因为列太多。
  • 查询正确的表时是否使用了索引?
  • 我不明白你为什么在这里使用左连接的说法。不是说这是错误的,我只是不明白 “我需要在 table_left 中包含 table_right 中没有行的结果” - 左连接不会完全做到这一点。左连接只是连接来自table_left 的行,但如果不存在来自table_right 的结果仍将包括在内。您能否包括架构、示例数据和预期输出?确保在两个表上都有一个关于pk_id 的索引——可能是namepk_id 上的一个复合索引。我们确实需要查看更多数据才能正确回答这个问题。
  • 我添加了 2 个示例表、我的查询和预期结果。我希望这会有所帮助。

标签: mysql


【解决方案1】:

如果您需要返回 table_left 中的所有结果(不管它是否匹配),那么左连接是正确的,就像您正在做的那样,所以不要担心尝试切换它。

查询大约需要 1 分钟,我认为这是因为它对所有列进行了完全外部联接。我不需要所有的列。

让我们明确一点:连接对您在连接条件中列出的列进行操作:在本例中为 table_right.fk_id 和 table_left.pk_id。 但是,您是正确的,非常大的表将需要更长的时间来处理。如果您不需要其余列,最好在进行任何连接之前将它们排除,因为输出表的宽度会更小(这意味着返回该输出表时的速度会提高)。

当尝试提高连接性能时,MySQL 的经验法则是使用索引。用外行的话来说,索引基本上告诉数据库使用特定列(或列)作为对表的查找。添加索引后的速度提升让我惊叹不已。

我强烈建议在这种情况下使用索引。这是用于设置这些的great tutorial。祝你好运!

【讨论】:

  • 感谢 S.S. 的建议。我会看看索引。仅供参考,我在原始问题中添加了 2 个示例表、我的查询和预期结果。
【解决方案2】:

左连接没问题:

SELECT table_left.pk_id, table_left.name
FROM table_left 
LEFT JOIN table_right on table_right.fk_id = table_left.pk_id
WHERE table_right.name LIKE '%searchTerm%' OR table_left.name LIKE '%searchTerm%'

查询大约需要 1 分钟

该性能问题与联接类型无关。

很可能是“双端”通配符导致查询时间过长。这些不会通过使用索引来改进。您的经验法则:

  1. 使用 equal 优先于 LIKE
  2. 使用 LIKE 'something%' 优先于 LIKE '%something'
  3. 使用 LIKE '%something' 优先于 LIKE '%something%
  4. 如果您使用 LIKE '%something%,不要指望闪电般的快速响应时间

说真的,您需要重新考虑使用通配符来提高性能。您可以使用尾随通配符获得对查询的索引支持,如果您在列的反面构建索引,则可以获得对前导通配符的索引支持,但您需要全文索引(和不同的查询)才能获得对(相当于)双端通配符的任何索引支持。


以前:

我使用外连接是因为我需要在 table_left 中包含结果 table_right 中没有行。

如果您仅通过 WHERE table_right.name like '%entered search value%' 过滤,这根本没有意义

只有从 table_right 到 table_left 匹配的行可以从 where 子句返回。

【讨论】:

  • 如果我使用 INNER JOIN 并搜索“Microsoft”,那么它不会返回空结果吗?我仍然希望微软被退回(即使他们在 table_right 中没有记录)
  • 是的,很抱歉您使用 OR 移动了目标帖子,但您的表现与加入无关,甚至与您正在使用的索引 IF LIKE '%...%' 无关
【解决方案3】:

我认为你的解释和你想要得到的东西过于夸张了。如果我对您的问题的解释是正确的,那么您需要 LEFT JOIN。您希望从 LEFT 表中不考虑右边,但如果右边恰好符合您的术语,则根据需要获取条目。

您的查询将条件应用于同一个左表字段,而不是左右。

为了在加入具有 60 个(或更多)列的右侧表时帮助优化您的查询,但您只是比较 FK_ID 及其“Job_Details”(根据您的测试人员和开发人员查询示例),我会构建仅针对这两列的复合索引,因此可以针对连接和搜索进行优化……索引(FK_ID,Job_Details)。这样,引擎可以直接从 INDEX 中获得答案,而不必返回到所有 60 列都存在的原始数据页面。 Table_Left 还应该在 (PK_ID, Name) 上有一个复合索引以进行优化。

现在,您基于 '%someValue%' 进行搜索,您需要进行全文搜索,因为前导 '%' 意味着您不知道字符串前面有多少个字符,也不知道后面有多少个字符,只要搜索字符串在查询中的某个位置。除非您需要,否则我建议不要使用前导 '%',但这是您的决定。

select
      LT.PKID,
      LT.Name
   from
      Table_Left LT
         LEFT JOIN Table_Right RT
            on LT.PK_ID = RT.FK_ID
           AND RT.JOB_Details LIKE 'Tester%'
   where
         LT.Name Like 'Tester%'
      OR NOT RT.FK_ID IS NULL

因此,这将根据左表的名称为“Tester”为您提供 或具有“测试员”工作详细信息的相应右表。但我认为这不是最佳选择。

但是,我实际上建议在 (Job_Details, FK_ID) 上提供不同的索引,然后使用 DISTINCT 执行 UNION 查询。

select DISTINCT
      LT.PKID,
      LT.Name
   from
      Table_Left LT
   where
      LT.Name Like 'Tester%'
UNION
select 
      LT.PKID,
      LT.Name
   FROM
      Table_Right RT
         JOIN Table_Left LT
            on RT.FK_ID = LT.PK_ID
   where 
      RT.JOB_Details LIKE 'Tester%'

这样,左表在按名称查找时针对其独特性进行了优化,而右表通过搜索其 JobDetails 并获取相应的左表名称信息进行了优化。

【讨论】:

    猜你喜欢
    • 2013-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-12
    • 2011-04-22
    • 1970-01-01
    • 2023-03-04
    • 1970-01-01
    相关资源
    最近更新 更多