2

我正在连接到一个我无法管理的数据库,并且我编写了一个查询,该查询在两个表之间进行了左连接 - 一个很小,一个大几个数量级。在某些时候,数据库返回了这个错误:

表 '/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:

请专注于回答我的实际问题,而不是尝试重写我的原始查询。

4

1 回答 1

1

左连接的东西是红鲱鱼。

然而,临时表空间不足是一个实际问题。但是你加入的顺序没有区别。唯一重要的是 MySQL 必须处理多少行。

这让我想到了 LIMIT 命令:

这是问题所在:

它为了获得您要求的单行,MySQL 必须对整个记录集进行排序,然后获取最上面的记录集。为了对其进行排序,它必须将其存储在内存中或磁盘上。这就是你空间不足的地方。您请求的每一列都存储在磁盘上,用于整个表,然后进行排序。

这很慢,非常慢,并且会占用大量磁盘空间。

解决方案:

您希望 MySQL 能够使用索引进行排序。但在您的查询中它不能。它使用索引作为连接引用,而 MySQL 每个查询只能使用一个索引。

您甚至在排序列上有索引吗?先试试那个。

另一种选择是做一个单独的查询,在这里你只选择大表的 ID,LIMIT 1。然后临时表要小得多,因为它只有 ID,没有所有其他列。

Once you know the ID then retrieve all the columns you need directly from the tables. You can do this in one shot with a subquery. If you post your query I could rewrite it to show you, but it's basically ID = (SELECT ID FROM ..... LIMIT 1)

于 2012-08-31T07:47:52.797 回答