【问题标题】:Perform SELECT on both tables before JOIN在 JOIN 之前对两个表执行 SELECT
【发布时间】:2014-07-08 15:42:17
【问题描述】:

我没有太多使用 JOIN 的经验,也不是 MySQL 方面的专家。

我想要做的是在执行 JOIN 之前对两个表进行 SELECT。为此,我尝试使用括号,但语法不正确。

例如,在 JOIN 之前不做 SELECT:

DELETE tbA.* FROM tbA
INNER JOIN tbB
ON tbA.id_tbB = tbB.id_tbB
WHERE tbB.dateCol<'2014-01-01 00:00:00'

这可行,但需要太多时间。我正在尝试做的事情,但没有成功,是这样的:

DELETE tbA.* FROM (SELECT * FROM tbA WHERE tbA.id_tbB<=id_max)
INNER JOIN (SELECT * FROM tbB WHERE tbB.id_tbB<=id_max)
ON tbA.id_tbB = tbB.id_tbB
WHERE tbB.date<'2014-01-01 00:00:00'

我试图将其综合到基本问题中......如果有人认为我应该提供更多信息(我想要做什么,索引,外键......)我很乐意这样做,但基本上我只想知道如何在 INNER JOIN 之前对两个表进行 SELECT。

我想这样做是因为我的表有大量记录(表 A ~ 1 亿,表 B ~ 40000)。之前进行选择会将两个表上的记录量减少到 10%,并有望显着减少 INNER JOIN,从而减少整体查询。

谁能指出错误?谢谢!

【问题讨论】:

  • 如何将 where 子句移到 join 本身?我不确定执行计划是否正在执行连接(大笛卡尔),然后在连接之前发生过滤或过滤......例如DELETE tbA.* FROM tbA INNER JOIN tbB ON tbA.id_tbB = tbB.id_tbB and tbB.dateCol&lt;'2014-01-01 00:00:00'
  • 感谢您的评论。但是,在您的建议中,我没有看到将数据减少到 10% 的条件 (tbB.id_tbB

标签: mysql select join inner-join


【解决方案1】:

怎么样

DELETE FROM tbA WHERE (select tbB.date from tbB where tbA.id_tbB = tbB.id_tbB) > '2014-01-01 00:00:00'

或替代

DELETE FROM tbA WHERE tbA.id_tdB IN (
     select tbB.id_tbB from tbB where tbB.date > '2014-01-01 00:00:00'
)

如果您在 tbA 中的 id_tdB 上有索引或外键,我会假设最后一个是最快的。

【讨论】:

  • 我的猜测是 select tbB.date from tbB where tbA.id_tbB = tbB.id_tbB 将首先执行,对吧?在这种情况下,WHERE 子句正在引用 tbA...这不会失败吗?
  • 如果我没有犯语法错误,我认为这些都不会失败:)
  • 是的,tbA中的id_tbB其实是一个指向tbB.id_tbB的外键。我会试着让你知道(我正在等待最后一个实验完成,然后我需要重新导入整个数据库并重试)。
  • 感谢@beiller 的建议,这就是我得到的。第一个抱怨语法。第二个有效,需要 1100 秒才能完成。与在 JOIN 之前不使用任何 SELECT 相比(正如我在问题中建议的那样),它需要 869,因此实际上需要更长的时间。不知道为什么,但在我看来,在您的建议中,我们正在对 tbB 执行 SELECT,并使用 IN 子句而不是 JOIN。我认为,由于 tbA 没有在 IN 之前应用 SELECT 子句,因此 IN 子句占用了 1 亿行中的每一行(tbA 中的原始行),可能这就是为什么?
  • 有趣。也许只尝试 """ select tbB.id_tbB from tbB where tbB.date > '2014-01-01 00:00:00' """ 看看需要多长时间。 tbB.date 上有索引吗?在我看来,您说 tbB 的行数较少。此外,您的解释没有太大意义,因为时差没有显着差异。
【解决方案2】:

您所做的应该可以,只是缺少一些重要的语法元素。你可以加入一个SELECT,这是一个叫做派生表(virtual table)的表。您必须命名派生表,以便可以引用它。您不能从派生表中删除,因为它是虚拟表,它只存在于内存中。所以你需要在你的选择中放置一个物理表。

试试这样的:

DELETE tbA FROM tbA 
INNER JOIN (SELECT * FROM tbB WHERE tbB.id_tbB<=id_max AND tbB.date<'2014-01-01 00:00:00') AS tbB_filter
ON tbA.id_tbB = tbB_filter.id_tbB
WHERE tbA.id_tbB<=id_max

派生表可以使事情变得更快,因为它预先过滤并将选择加载到内存中。因为它是一个派生表,它没有索引,所以如果你的派生表变得太大,它实际上会减慢速度。从派生表中仅选择您需要的字段以保持较低的内存占用。您始终可以在最后进行附加连接以获得所需的其他字段。

【讨论】:

  • 我比较了三种不同的:
  • 感谢您的建议@Brent。我已经尝试过了,它奏效了。耗时 839 秒。但是,与根本不使用任何选择(如问题文本中所建议的)相比,它并不是真正的显着改进(869 秒)。在我看来,在您的示例中,我们仅在 tbB 中执行 SELECT,但仍在尝试使用 tbA 中的 1 亿条记录进行 JOIN。我不知道改进是否来自从 tbB 中的 40000 行到虚拟表 tbB_filter 中的 4000 行。我认为在 tbA 上执行 SELECT 也应该带来更大的改进。让我知道你的想法和谢谢
  • tbA SELECT 非常简单,我不认为将它分开会做很多事情。 MySQL 应该在 JOIN 之前对 tbA 应用 WHERE 过滤器。您可以尝试将 WHERE 条件移动到 JOIN 条件中。也只为 tbB_filter 选择 id_tbB 字段,而不是 *。您应该使用 SELECT 而不是 DELETE 来测试性能。
【解决方案3】:

好吧,我猜这个问题的实际答案(或至少其中一个):

如何在 JOIN 之前对两个表执行 SELECT?

是通过使用虚拟表...类似于:

SELECT tbA_filter.* FROM (SELECT tbA.* FROM tbA WHERE tbA.id_tbB<=id_max) AS tbA_filter 
INNER JOIN (SELECT * FROM tbB WHERE tbB.id_tbB<=id_max AND tbB.date<'2014-01-01 00:00:00') AS tbB_filter
ON tbA_filter.id_tbB = tbB_filter.id_tbB
WHERE tbA_filter.id_tbB<=id_max

但是,如果它是一个 DELETE 操作(如我的示例),则不可能,因为删除虚拟表上的内容是没有用的(因为它是一个虚拟表,而不是真正的原始表)。

考虑到虚拟表没有索引也很重要......因此尝试使用两个没有索引的表进行 JOIN 可能不明智,因为这将花费很长时间(尤其是在具有令人难以置信的大量行)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-12-05
    • 2011-12-14
    • 1970-01-01
    • 1970-01-01
    • 2020-12-24
    • 2013-01-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多