sql 查询连接多个表 - 太慢(8 个表)

Posted

技术标签:

【中文标题】sql 查询连接多个表 - 太慢(8 个表)【英文标题】:sql query joins multiple tables - too slow (8 tables) 【发布时间】:2010-10-22 19:18:04 【问题描述】:

我正在尝试将 8 个表合并为一个以创建其他应用程序使用的索引,我的查询是这样的:(我的 mysql 技能非常业余)

SELECT t1_id, t2_name, t3_name, t4_name, t5_name, 
       t6_name, t7_name, t8_name, t9_name 
FROM t1 
  LEFT JOIN t2 ON (t1_id = t2_id) 
  LEFT JOIN t3 ON (t3_id = t1_id) 
  LEFT JOIN t4 ON (t4_id = t1_id)
  LEFT JOIN t5 ON (t5_id = t1_id)
  LEFT JOIN t6 ON (t6_id = t1_id) 
  LEFT JOIN t7 ON (t7_id = t1_id)
  LEFT JOIN t8 ON (t8_id = t1_id)
  LEFT JOIN t9 ON (t9_id = t1_id)

执行时我什至看不到查询结果,有什么方法可以加快速度吗? :) 任何形式的帮助都值得赞赏,但最好只有一个查询(在应用程序规则之外)

提前致谢

【问题讨论】:

【参考方案1】:

我有一个类似的问题,几个查找表连接到一个所有 id 字段都被索引的大表。为了监控联接对查询时间执行的影响,我多次运行查询(限制为前 100 行),每次都将联接添加到附加表中。加入 12 个表后,查询执行时间没有明显变化。当我加入第 13 个表时,执行时间跃升至 1 秒;第14桌4秒,第15桌20秒,第16桌90秒。

Keijro 建议使用相关子查询而不是连接,例如

SELECT t1_id, 
        (select t2_name from t2 where t1_id = t2_id), 
        (select t3_name from t3 where t1_id = t3_id), 
        (select t4_name from t4 where t1_id = t4_id), 
        (select t5_name from t5 where t1_id = t5_id), 
        (select t6_name from t6 where t1_id = t6_id), 
        (select t7_name from t7 where t1_id = t7_id), 
        (select t8_name from t8 where t1_id = t8_id), 
        (select t9_name from t9 where t1_id = t9_id)  FROM t1

显着提高了查询性能。事实上,子查询似乎并没有延长执行查询的时间(查询几乎是即时的)。

我有点惊讶,因为我认为相关子查询的性能比连接差。

【讨论】:

如果可以的话,我会给你更多的 +1。我遇到了完全相同的问题,这个解决方案非常有效。来自 MSSQL/Oracle 背景的感知智慧是尽可能避免相关子查询,这是非常违反直觉的,但它确实有效。阅读它似乎可能是因为 MySQL 将连接处理为嵌套循环。 我来自 15 个表连接。即使有索引,查询仍然需要我从 50 个结果限制中花费 1 或 2 秒。我将尝试使用您的建议重写大方法。谢谢!【参考方案2】:

根据表中的数据量,您可能需要在要连接的列上放置索引。查询速度慢通常归结为在正确的位置缺少索引。

还有:

LEFT JOIN 比 INNER JOIN 慢(尽管这取决于您的具体操作) - 您可以使用内部连接完成您正在寻找的内容吗?

【讨论】:

可以很好地解释内连接和左连接之间的区别:内连接不包括未找到数据的行,而左连接(或外连接)则包含。也就是说,如果说 t8 缺少与 t1_id 对应的 id,则根本不会包含该行(即使还有其他具有该 id 的表) 同意这两点。使用 INNER JOINS 并确保您要加入的字段有索引。 但请参阅下面有关使用相关子查询的答案。我有一个 15 个连接表(所有内部连接都用于解码主索引),如果可以将 10 个连接放入相关子查询而不是连接中,它的运行速度会快 1000 倍【参考方案3】:

如果您可以发布查询的解释计划,这将有所帮助。

但是,首先,您对连接中使用的所有字段都有索引? 类似CREATE INDEX ix_t2_id on t2 (t2_id, t2_name);

您可以执行类似

的操作,而不是连接
SELECT t1_id, 
    (select t2_name from t2 where t1_id = t2_id), 
    (select t3_name from t3 where t1_id = t3_id), 
    (select t4_name from t4 where t1_id = t4_id), 
    (select t5_name from t5 where t1_id = t5_id), 
    (select t6_name from t6 where t1_id = t6_id), 
    (select t7_name from t7 where t1_id = t7_id), 
    (select t8_name from t8 where t1_id = t8_id), 
    (select t9_name from t9 where t1_id = t9_id) 
FROM t1 

但是,对于一个好的查询计划器,这应该与连接没有什么不同。

【讨论】:

感谢您的回答,这是解释计划:1 SIMPLE r ALL NULL NULL NULL NULL 4977 1 SIMPLE m ref release release 4 main.r.r_id 2 1 SIMPLE t ref release release 4 main。 r.r_id 2 1 SIMPLE s ALL NULL NULL NULL NULL 4889 1 SIMPLE mu ref release release 4 main.r.r_id 2 1 SIMPLE n ALL NULL NULL NULL NULL 6 1 SIMPLE q ALL NULL NULL NULL NULL 13 1 SIMPLE o ref release release 4 main.r.r_id 2 1 SIMPLE g ref release release 4 main.r.r_id 2 我不确定内联选择是提高 SQL 性能的好方法。 有些书称它们为相关子查询。它们是 From 之前的其他 select 语句中的选择;它们是逐行评估的,因此对于大型数据集,它们的效率非常低。 没错,它们大多应该被避免,但我有几次它们对性能做出了很大贡献,所以在某些情况下应该考虑它们。【参考方案4】:

我们在谈论多少数据?可能是您有大量数据,并且由于 where 子句在查询过程结束时运行,您在过滤之前加入了大量数据。

在这种情况下,最好尽快过滤数据,因此如果您可以在第一个内部选择中限制来自 T1 的数据,则所有其他连接将连接到更有限的数据集。

Select <your fields> from
(
Select * from t1 where t1_id = t1_value
) t1

Inner join t2
on t1.ID = t2.ID
...

如果不是大量数据;检查您的索引是否正确,然后检查服务器类型;索引碎片;磁盘队列等

【讨论】:

是否还有要检查的 t1_value?他/她在问题中没有这样说。【参考方案5】:

如果你需要t1的所有行,而你在其他表的主键(我猜也是聚集索引)上留下join,那么查询速度是没有办法提高的。

要提高性能,您要么需要减少结果集,要么执行一些令人讨厌的技巧(例如,制作数据的非规范化副本)。

【讨论】:

【参考方案6】:

根据您的查询计划,我可以得出结论,称为 snq 的表在它们要连接的字段上没有索引。

由于这些表中有很多行(大约400,000 行在他们的笛卡尔积中)并且MySQL 的唯一方法是使用JOIN 是使用NESTED LOOPS,它真的需要很长时间。

在这些表上创建索引或将连接字段定义为PRIMARY KEY

【讨论】:

【参考方案7】:

如我所见,t1 表是与所有表连接的表,而不是将它们放在具有如此多连接的单个查询中,您可以尝试类似这样的不同查询的联合。

SELECT  t1_id, t2_name 
FROM    t1 LEFT JOIN t2 ON (t1_id = t2_id)
union 
SELECT  t1_id, t3_name 
FROM    t1 LEFT JOIN t3 ON (t1_id = t3_id)

但是,在这种情况下,您将获得的结果将不是 8 列,而是只有 1 列。不确定这是否适合您。

在您实施的任何解决方案中,您必须做的另一件事是 - 在所有表上创建适当的索引。索引列的最佳实践是在最常用于连接或 where 子句的列上创建它。

【讨论】:

【参考方案8】:

当您使用的数据集太大以至于在执行连接时超出了工作内存时,连接速度会显着降低。然后,Postgres 将在运行过程中将其工作保存到磁盘。这就是为什么您可能会在第 n 次连接之后才看到速度变慢,而不管您要连接哪些表,或者您是否正确配置了索引。

在我的例子中,EXPLAIN 只显示了几万行,没什么好写的。但我了解到,即使在加入期间发生减速,加入也可能不是问题。在我的例子中,罪魁祸首原来是一个非常大的 uuid[] 列,其中包含很多条目。我从查询中排除了这一列,它加快了一切速度。

【讨论】:

【参考方案9】:

根据您的 SQL Server 版本,简单地将查询放入存储过程可能会产生很大的不同。先尝试其他优化后再试试这个。(是的,我知道有缓存的执行计划和其他内部服务器优化,但根据我的实际经验,存储过程可以更快地执行。)

【讨论】:

以上是关于sql 查询连接多个表 - 太慢(8 个表)的主要内容,如果未能解决你的问题,请参考以下文章

SQL怎么连接查询2个表?

sql server链接查询

SQL Server表连接

一个sql连接表的问题

连接两个表并从一列返回多个匹配项的 SQL 查询?

SQL - 内连接 2 个表,但如果 1 个表为空,则返回所有表