2

管理できないデータベースに接続しており、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

(2 番目のテーブルの 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 はクエリごとに 1 つのインデックスしか使用できません。

ソート列にインデックスさえありますか?まずそれを試してください。

もう 1 つのオプションは、別のクエリを実行することです。この場合、大きなテーブルの ID である LIMIT 1 のみを選択します。この場合、一時テーブルは他のすべての列を含まない ID だけであるため、非常に小さくなります。

ID がわかったら、必要なすべての列をテーブルから直接取得します。これは、サブクエリを使用して 1 回で実行できます。クエリを投稿すると、表示するために書き直すことができますが、基本的にはID = (SELECT ID FROM ..... LIMIT 1)

于 2012-08-31T07:47:52.797 に答える