【问题标题】:LEFT JOIN takes too long when SORTING results with ORDER BY使用 ORDER BY 对结果进行排序时,LEFT JOIN 花费的时间太长
【发布时间】:2012-04-11 15:35:02
【问题描述】:

这是我在stackoverflow中的第一个问题,通常我习惯于在互联网上搜索答案,但这次我找不到这个问题的任何答案。

只是我的问题是查询需要太长时间才能执行,而它是 2 个表之间的简单连接

我将首先发布我的查询,然后我将发布有关我的系统的更多详细信息:

SELECT * FROM tbl_item 
LEFT JOIN (SELECT * FROM tbl_item_details) AS tbl_item_details 
ON tbl_item.item_id = tbl_item_details.item_details_item_id 
WHERE item_active = 1 ORDER BY item_views DESC LIMIT 0,5

这是我的表格结构:

CREATE TABLE `tbl_item` (
`item_id` int(11) NOT NULL AUTO_INCREMENT,
`item_views` int(11) NOT NULL,
`item_active` tinyint(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`item_id`)
) ENGINE=InnoDB AUTO_INCREMENT=821 DEFAULT CHARSET=utf8

tbl_item_details:

CREATE TABLE `tbl_item_details` (
`item_details_id` int(11) NOT NULL AUTO_INCREMENT,
`item_details_title` varchar(255) NOT NULL,
`item_details_content` longtext NOT NULL,
`item_details_item_id` int(11) NOT NULL,
PRIMARY KEY (`item_details_id`),
KEY `itm_dt_itm_id` (`item_details_item_id`),
CONSTRAINT `tbl_item_details_ibfk_1` FOREIGN KEY (`itm_dt_itm_id`) REFERENCES `tbl_item` (`itm_id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=364 DEFAULT CHARSET=utf8

这里是 EXPLAIN 查询输出:

id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY tbl_item ALL NULL NULL NULL NULL 358 使用 where;使用临时的;使用文件排序 1 主要 全部 NULL NULL NULL NULL 358 2 衍生 tbl_item_details 全部 NULL NULL NULL NULL 422

每个表只有 350 行,而大表 (tbl_item_details) 为 1.5 MB,因此您会看到这些表非常小。

基本上,上述查询在以下系统上执行大约需要 4 秒:

CPU:Intel(R) Pentium(R) 4 CPU 3.20GHz(2 个 CPU),~3.2GHz 内存:3 GB Mysql:5.1.33(包含在 wamp 中)

之前,任何人都建议这里的解决方案是我尝试过的,有效的,无效的:

有效且查询花费的时间要少得多(0.06 秒):

  • 我尝试删除 ORDER BY 我尝试从中删除 item_details_content
  • 我尝试使用 INNER JOIN 进行选择,但我尝试过它有效
  • 在连接中切换表,使 INNER 表变为 OUTER,反之亦然

我不能使用 INNER JOIN,因为 tbl_item 中可能有行在 tbl_item_details 中没有匹配项,我想要这些记录

我尝试过但不起作用的事情:

  • 我尝试向 item_views 添加索引(不起作用)
  • 我尝试删除外键约束
  • 我尝试将表格引擎转换为 MyIsam

很明显,当 mysql 对 item_details_content 中的日期和面对(相对)较大的数据进行排序时,就会出现问题,所以如果我们去掉其中的一件事(排序或 item_details_content 列),它就可以正常工作。

但问题是,这不应该发生!因为该表的数据非常小,考虑到它只有 350 行,总共 1.5 MB! mysql 应该能够处理比这更多的数据。

请,在建议对查询结构进行重大更改之前,恐怕这是不可能的,因为我已经在这个框架上工作了一段时间并且查询是动态生成的,对查询的更改可能意味着几天工作,但总是欢迎您的建议。

P.S:我在一个强大的服务器(核心 i7 和 8 GB RAM)上尝试了这个查询,它花了 0.3 秒,对于这样的数据库来说仍然太长了

谢谢一百万

【问题讨论】:

  • 对不起,我得走了,但有一个快速提示:我认为问题在于你没有做“简单的加入”,因为你加入了一个子查询。该查询的结果集没有索引,因为它是动态创建的。只有表上的连接(有索引!)是快速的。加入tbl_item_details 而不是该表的SELECT
  • 考虑向 tbl_items.item_views 列添加索引。这应该会提高查询性能。
  • Nanne,感谢您的回答,但连接表的条件如何,实际上该表的第一个表中的每一行应该有 2 行或更多行(每种语言的一行)。那么像 ON tbl_item.item_id = tbl_item_details.item_details_item_id 和 tbl_item_details.item_language = VALUE 一样安全吗??
  • bfavaretto,它确实不像我之前在问题中提到的那样

标签: mysql sql-order-by left-join


【解决方案1】:

你试过了吗:

SELECT * FROM tbl_item 
LEFT JOIN tbl_item_details 
ON tbl_item.item_id = tbl_item_details.item_details_item_id 
WHERE item_active = 1 ORDER BY item_views DESC LIMIT 0,5

我真的认为您不需要只从 tbl_item_details 中选择 * 的嵌套子查询——只需使用表即可。

【讨论】:

  • 我不确定你所说的索引是什么意思,你说的是我排序的字段吗!因为是的,我已经尝试了一个索引并且它没有改变任何东西,或者如果你的意思是我在第二个表中没有索引,那么是的,它有一个索引并且它也有一个主键(我不是假设你的意思是我应该在 item_details_content 字段上添加全文索引
  • 啊,我只是想添加一个索引,但我忘记了将字段声明为主键会隐式索引该字段,所以不要介意索引。消除子查询有帮助吗?
  • @Al-Kateb 最后一件事...如果您的应用程序没有很多同时更新表中不同行的并发用户,那么也许您应该尝试使用 MyISAM引擎而不是 InnoDB。这里有一点关于差异的说明,但据我了解,InnoDB 使用行锁定与表锁定,并且 CPU 效率也较低。 mikebernat.com/blog/MySQL_-_InnoDB_vs_MyISAM
  • 感谢您的建议,我过于依赖外键约束,但消除子查询肯定有帮助,我正在用 PHP 构建一个框架,所以当我第一次决定加入子查询时我正在考虑在子查询中设置 where 条件,但我不知道它会对性能造成太大影响,所以我将删除它并加入表而不是因为它只是不值得。非常感谢大家的回答
  • 啊,是的,fk 约束有点反对 myisam 的想法。也许您可以有一个将数据插入临时表的存储过程,这样您就不需要子查询(不确定这是否对您的数据有意义)。实际上,我有点惊讶它对查询的影响如此之大——我以前使用过带有连接的子查询,只是我通常在 where 子句而不是连接部分中使用它们,这可能就是原因。无论如何,祝你好运。
猜你喜欢
  • 2020-01-31
  • 2021-01-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多