我正在连接到一个我无法管理的数据库,并且我编写了一个查询,它在两个表之间进行了左连接 - 一个小表和一个几个数量级的大表。在某些时候,数据库返回了这个错误:
表'/tmp/#sql_some_table.MYI'的密钥文件不正确;尝试修复它
我联系了管理员,我被告知我收到了这个错误,因为我正在进行左连接错误,我不应该将一个小表连接到一个大表,我应该反转连接顺序。他们给出的原因是,当我完成我的方式时,MySQL将尝试创建一个太大的临时表,查询将失败。他们的解决方案在其他地方失败了,但这在这里并不重要。
我发现他们的解释很奇怪,所以我对我的查询进行了解释:
id = '1'
select_type = 'SIMPLE'
table = 'small_table'
type = 'ALL'
possible_keys = NULL
key = NULL
key_len = NULL
ref = NULL
rows = '23'
Extra = 'Using temporary; Using filesort'
id = '1'
select_type = 'SIMPLE'
table = 'large_table'
type = 'ref'
possible_keys = 'ID,More'
key = 'ID'
key_len = '4'
ref = 'their_db.small_table.ID'
rows = '41983'
Extra = NULL
(第二个表中的41983行对我来说不是很有趣,我只需要最新的记录,这就是我的查询最后有order by large_table.ValueDateTime desc limit 1
的原因。)
我小心翼翼地按照管理员自己告诉我的列进行选择,应该保存唯一值(因此我假设已编入索引),但似乎他们没有索引这些列。
我的问题是 - 正在以我的方式进行连接('small_table LEFT JOIN large_table')一般的不良做法,或者可以通过正确的索引使这些查询成功执行?
编辑: 这是查询的样子(这不是实际查询,但类似):
select large_table.ValueDateTime as LastDate,
small_table.DeviceIMEI as IMEI,
small_table.Other_Columns as My_Names,
large_table.Pwr as Voltage,
large_table.Temp as Temperature
from small_table left join large_table on small_table.ID = large_table.ID
where DeviceIMEI = 500
order by ValueDateTime desc
limit 1;
基本上我正在做的是尝试获取设备的最新数据,因为电压和温度会随着时间的推移而变化。 DeviceIMEI,ID和ValueDateTime应该是唯一的,但不会被编入索引(就像我之前说过的,我不管理数据库,我只有读取权限)。
编辑2:
请专注于回答我的实际问题,而不是尝试重写原始查询。
答案 0 :(得分:1)
左连接是一个红色的鲱鱼。
然而,这是临时表空间不足的实际问题。但是你加入的顺序没有区别。唯一重要的是MySQL必须使用多少行。
这让我看到了LIMIT命令:
问题在于:
为了获得你要求的单行,MySQL必须对ENTIRE记录集进行排序,然后获取最高记录集。为了对它进行排序,它必须将其存储在内存或磁盘上。那就是你的空间不足的地方。您请求的每一列都存储在磁盘上,整个表中,然后排序。
这很慢,非常慢,并占用大量磁盘空间。
解决方案:
您希望MySQL能够使用索引进行排序。但在你的查询中它不能。它使用连接引用的索引,MySQL每个查询只能使用一个索引。
你甚至在排序列上有索引吗?先尝试一下。
另一种选择是进行单独的查询,只选择大表的ID,LIMIT 1.然后临时表变小,因为它只有ID而没有其他所有列。
知道ID后,直接从表中检索所需的所有列。您可以使用子查询一次性执行此操作。如果您发布查询我可以重写它以向您显示,但它基本上是ID = (SELECT ID FROM ..... LIMIT 1)