如何通过内部连接优化自连接选择到另一个表

Posted

技术标签:

【中文标题】如何通过内部连接优化自连接选择到另一个表【英文标题】:How to optimize a self-joining select with an inner join to another table 【发布时间】:2020-03-06 18:07:46 【问题描述】:

我有一个留言板,其中的消息、线程和留言板都存储在一个“页面”表中。我想列出所有消息及其线程和板的祖先。对此的自联接很快,但是当我使用内部联接将消息作者包括到另一个表时,查询速度会减慢到几秒钟。有什么方法可以更好地构造查询吗?

数据库如下所示:

pagetypes
type        | typename
———————————————————
15           board
16           thread
17           message 


pages
pageid  |  type | parentid    | authorid | title                
————————————————————————————————————————————
1          15      null          1        "BigBoard"
2          16      1             1        "Introductions"
3          17      2             2        “Hello everyone!”
4          17      2             1        “Welcome!”
5          16      1             1         “News and Gossip”
6          17      5             3        “Whats new?”

users
id    | name
————————————————————
1       "Peter"
2       "Paul"
3       "Mary"

我的选择查询是:

select p.title as message_title, u.name, t.title as thread_title, b.title as board_title
from pages as p
join pages as t on p.parentid=t.pageid
join pages as b on t.parentid=b.pageid 
join users as u on p.authorid=u.id
where p.type=17

结果如下:

message_title       |   name    |   thread_title       |    board_title
-----------------------------------------------------------------------
“Hello everyone!”       Paul        "Introductions"         "BigBoard"
“Welcome                Peter       "Introductions"         "BigBoard"
“What’s new?””          Mary        "News and Gossip"       "BigBoard"

如果我让用户离开那里,查询(在 40 万页上)是 30 毫秒。将用户添加到请求后,查询最多需要 3 秒。

以下是表格说明:

CREATE TABLE `pages` (
  `pageid` int(10) unsigned NOT NULL DEFAULT '0',
  `parentid` int(10) unsigned NOT NULL DEFAULT '0',
  `authorid` int(10) unsigned NOT NULL DEFAULT '0',
  `type` smallint(5) unsigned NOT NULL DEFAULT '0',
  PRIMARY KEY (`pageid`),
  KEY `text_key` (`parentid`,`type`),
  KEY `type_key` (`type`),
  KEY `byAuthor` (`authorid`,`type`)
) 

CREATE TABLE `users` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  UNIQUE KEY `name` (`name`)
)

【问题讨论】:

请为您的查询添加 EXPLAIN 结果并为您的表格显示表格以查看您已经拥有哪些索引 根据 nbk 的建议,我为包括索引的表添加了 sql。我不确定其他索引可能会有所帮助。 请在代码问题中给出minimal reproducible example--剪切&粘贴&可运行代码;具有期望和实际输出(包括逐字错误消息)的示例输入(作为初始化代码);标签和版本;明确的规范和解释。对于包含 DBMS 和 DDL 的 SQL,其中包括约束、索引和表格初始化。对于包括 EXPLAIN 结果和统计信息的 SQL 性能。 (约束、索引和计划至关重要。)在考虑发布之前请先研究一下。这包括 SQL 优化/性能的基础——直接导致索引、计划、统计和 SARGability。 How to Ask 感谢 philipxy 的宝贵建议。我没有意识到 EXPLAIN 的存在。我将开始用它分析查询并继续研究 SQL 优化/性能。 (My)SQL DBMS "KEY" 不代表 FK。约束不是索引。了解每个是什么。如果您有其他 FK 未隐含的 FK,请声明它们。 DBMS 可以使用 FK 进行优化。它们也有默认索引。阅读手册中有关 FK 的所有内容。 (以及您使用的所有其他功能。) PS 您可以学习重新性能的一件事是谷歌关于它的 SO 问题并查看人们被要求提供什么以及答案。也谷歌meta.SO。还有整本书。一些在网络上免费,例如 pdf。 Tips for asking a good SQL question 【参考方案1】:

我想我解决了。我向用户添加了一个 LEFT JOIN,使查询时间缩短到 30 毫秒。

select p.title as message_title, u.name, t.title as thread_title, b.title as board_title
from pages as p
join pages as t on p.parentid=t.pageid
join pages as b on t.parentid=b.pageid 
LEFT JOIN users as u on p.authorid=u.id
where p.type=17

【讨论】:

LEFT JOIN 返回 INNER JOIN 行加上由空值扩展的不匹配的左表行。因此,如果这有帮助,这会迫使 DBMS 遍历所有左表,那是因为它不是之前的,并且不适当的默认统计信息建议了其他一些计划。因此,如果您想了解正在发生的事情以及将来如何帮助自己,请根据 cmets 编辑您的问题。此外,这 2 个查询并不总是返回相同的结果,因此您实际上并没有修复第一个查询的性能。 感谢 philipxy 的建议 - 这是我第一次在这里发布问题,所以我当然还有改进的空间!根据查询并不总是返回相同的结果,我正在使用扩展查询对它们进行测试,该查询按创建日期对结果进行排序,因此结果应该相同? 看看我刚才给左连接的定义。当可能存在不匹配的左侧表行时,其结果与右侧不同。在这里,当/iff 某些作者不是用户时。但是缺少相关的 DDL 和数据。但是,如果作者是用户(例如,从 a 到 u 有一个 FK),您仍然应该使用 inner,因为它捕获了意图,因为不可能有不匹配的行。您应该找出原件速度慢的原因。原始文件甚至可能还不是很慢——缓存、测试之间的其他变化、实际上没有测量正确的东西等等。请阅读手册中的所有内容重新约束、索引和优化。

以上是关于如何通过内部连接优化自连接选择到另一个表的主要内容,如果未能解决你的问题,请参考以下文章

通过 WinSCP 使用 PuTTY 时如何选择要连接的自定义端口

如何通过内部连接使用 group by

如何通过 WiFi 直接连接将屏幕共享到另一台设备

如何利用Nginx的缓冲缓存优化提升性能

我怎样才能避免额外的内部连接来优化这个查询?

SQL_连接(Join),内部连接(INNER JOIN),左连接(LEFT JOIN ),右连接(RIGHT JOIN)完整外部连接(FULL OUTER JOIN),自连接(Self JOIN)(