[英]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.