簡體   English   中英

如何防止使用ON cond1或cond2的SQL INNER JOIN忽略鍵並進行全表掃描

[英]How to prevent a SQL INNER JOIN with ON cond1 OR cond2 to ignore keys and do a full table scan

我有一個簡單的查詢,無法正常工作。 盡管有索引,查詢的聯接部分仍會忽略它並進行全表掃描。 這是查詢

SELECT m0.id_field, 
       attr_73217_   
  FROM object_73195_ o
       INNER JOIN master_slave m0 
           ON  (   m0.id_object = 73130 
                OR m0.id_object = 82344) 
           AND (   m0.id_master = 73195 
                OR m0.id_master = 82413) 
           AND m0.id_slave_field = o.id 
ORDER BY
       o.id_order

EXPLAIN命令返回以下行:

id  select_type table   type    possible_keys                                                               key     key_len ref                       rows  Extra
1   SIMPLE      m0      ALL     id_object,id_master,id_slave_field,id_slave_field_2,id_object_2,id_object_3 \N      \N      \N                        2782  Using where; Using temporary; Using filesort
1   SIMPLE      o       eq_ref  PRIMARY                                                                     PRIMARY 8       project.m0.id_slave_field 1     Using where

如您所見,即使它是這樣創建的,它也不使用密鑰:

ALTER TABLE master_slave ADD INDEX (id_object,id_master,id_slave_field); 

有趣的是,如果我從SELECT部分中注釋掉m0.id_field ,那么首先選擇類型(由explain命令給出)變成range ,查詢開始使用鍵id_object_3 ,這也很重要-它現在掃描master_slave表中的行數較少。 但是要注意的是,我的select部分需要m0.id_field 我想我需要對索引做些什么,但是我不知道到底是什么。

編輯我試圖添加另一個像這樣的鍵:

ALTER TABLE master_slave ADD INDEX (id_field); 
ALTER TABLE master_slave ADD INDEX (id_object); 

但是EXPLAIN命令返回的行非常相同-沒有鍵和全表掃描。 整個問題是由select部分中的m0.id_field引起的。

編輯

我剛剛在master_slave表中添加了一堆索引:

ALTER TABLE master_slave ADD INDEX (id_field,id_object,id_master,id_slave_field);
ALTER TABLE master_slave ADD INDEX (id_object,id_field,id_master,id_slave_field);
ALTER TABLE master_slave ADD INDEX (id_object,id_master,id_field,id_slave_field);
ALTER TABLE master_slave ADD INDEX (id_object,id_master,id_slave_field,id_field);

每個索引都會減少掃描的行數。 我特別感謝kordirko

@Jacobian-這不是您問題的答案
或者可能只是部分答案。
我在這里寫是因為我的解釋太長,不適合評論。


如果select語句不包含m0.id_field ,則查詢僅引用m0表中的3個字段:id_object,id_master,id_slave_field。
由於該表上有這3列的覆蓋索引 ,因此顯而易見的選擇是掃描該索引而不是表。 索引(磁盤上的索引文件)比表小得多,並且讀取索引的成本比讀取表要少。


我們說當索引包含查詢檢索到的所有必需列時覆蓋索引 ,並且查詢可以直接從索引中檢索所有信息->請參閱: http : //en.wikipedia.org/wiki/Database_index#Covering_index


如果將m0.id_field添加到select子句,則沒有索引包含所有這4個列,在這種情況下,查詢必須從表中讀取此列的值。
它可以通過兩種方式實現:
1.使用索引過濾行,然后使用從索引獲得的主鍵訪問表中的行(逐行-隨機訪問)。
2.掃描整個表,而不觸碰任何索引

第一種方法在預期的行數較少(<5%可能小於表的10%)的情況下很好。 請記住,DBMS無法從磁盤讀取一行,它必須始終讀取整個頁面! 要獲得一行大小(例如50字節)的行,它必須讀取整個頁面,該頁面的大小為5k或10k或更大(頁面的長度取決於設置)。 有一些優化的可能,例如MySql,在掃描索引時,首先在內存中收集PK值,然后對它們進行排序,最后使用這些PK以升序掃描表,以最大程度地減少從磁盤檢索的頁數。 但這仍然是隨機訪問,它比順序讀取要慢(磁盤必須將磁頭搜索到隨機磁道,而不是逐軌讀取磁道)

如果預期的行數很大(在我們的例子中為表的34%),則使用第二種方法(掃描整個表)要比先掃描並過濾索引,然后對掃描結果進行排序,然后便宜得多使用從索引檢索的PK訪問表。 必須從磁盤讀取的磁盤頁面的最終數量較少(掃描索引還必須從磁盤讀取一些頁面)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM