Ho una query che mi sta dando problemi e non riesco a capire perché il Query Optimizer di MySQL si comporti così com'è. Ecco le informazioni di base:Perché MySQL non può ottimizzare questa query?
Ho 3 tavoli. Due sono relativamente piccoli e uno è grande.
Tabella 1 (molto piccole, 727 righe):
CREATE TABLE
ipa
(
ipa_id
int (11) AUTO_INCREMENT NOT NULL,
ipa_code
int (11) NULL DEFAULT
ipa_name
varchar (100) NULL DEFAULT
payorcode
varchar (2) NULL DEFAULT
compid
int (11) DEFAULT '2'
PRIMARY KEY (ipa_id
),
PRINCIPALIipa_code
(ipa_code
)) MOTORE = MyISAM
Tabella 2 (piccole, 59455 righe):
CREATE TABLE
assign_ipa
(
assignid
int (11) NOT NULL AUTO_INCREMENT,
ipa_id
int (11) NOT NULL,
userid
int (11) NON NULLO L,
username
varchar (20) PREDEFINITO NULL,
compid
int (11) PREDEFINITO NULL,
PayorCode
char (10) PREDEFINITO NULL
chiave primaria (assignid
),
UNIQUE KEYassignid
(assignid
,ipa_id
),
KEYipa_id
(ipa_id
)
) MOTORE = MyISAM
Tabella 3 (grandi, 24,711,730 righe):
CREATE TABLE
master_final
(
IPA
int (11) PREDEFINITO NULL,
MbrCt
smallint (6) DEFAULT '0',
PayorCode
varchar (4) STANDARD 'WC',
KEYidx_IPA
(IPA
)
) ENGINE = MyISAM DI DEFAULT
Ora per la query. Sto facendo un join a 3 vie usando le prime due tabelle più piccole per sommare essenzialmente la grande tabella su uno dei suoi valori indicizzati. Fondamentalmente, ottengo una lista di ID per un utente, SJOnes e interrogare il grande file per quegli ID.
mysql> spiegare
SELEZIONA master_final.PayorCode, sum (master_final.Mbrct) AS MbrCt
DA master_final
INNER JOIN IPA ON ipa.ipa_code = master_final.IPA
INNER JOIN assign_ipa ON ipa.ipa_id = assign_ipa.ipa_id
DOVE assign_ipa.username = 'SJones'
GROUP BY master_final.PayorCode, master_final.ipa \ G;
* ** * ** * ** * ** * 1. fila * ** * ** * ** * * * *
id: 1
select_type: SEMPLICE
tavolo: master_final
Tipo: TUTTI
possible_keys: idx_IPA
chiave: NULL
key_len: NULL
ref: NULL
righe:
Extra: Utilizzo temporaneo; Utilizzando filesort
* ** * ** * ** * ** * 2. riga * ** * ** * ** * ** *
id: 1
select_type: SEMPLICE 01.238.007,316 mila tabella: IPA
Tipo: rif
possible_keys: PRIMARIO, ipa_code
chiave: ipa_code
key_len: 5
rif: wc_test.master_final.IPA
righe: 1
Extra: Usando dove
* *** *** *** *** 3.fila * ** * ** * ** * ** *
id: 1
select_type: SEMPLICE
tabella: assign_ipa
Tipo: ref
possible_keys: ipa_id
chiave: ipa_id
key_len: 4
rif: wc_test.ipa .ipa_id
righe: 37
Extra: (! come 30 minuti) con cui
3 rows in set (0.00 sec)
Questa query richiede sempre. La spiegazione spiega perché, sta facendo una scansione completa della tabella sul grande tavolo anche se c'è un indice perfettamente buono. Non lo sta usando. Non lo capisco Posso esaminare la query e vedere che è solo necessario interrogare un paio di ID dal grande tavolo. Se posso farlo, perché l'ottimizzatore di MySQL non può farlo?
Per illustrare, qui sono gli ID associati 'SJones':
mysql> select nome utente, ipa_id da assign_ipa dove username = 'SJones';
+ ---------- + -------- +
| nome utente | ipa_id |
+ ---------- + -------- +
| SJones | 688 |
| SJones | 689 |
+ ---------- + -------- +
2 rows in set (0.02 sec)
Ora, posso riscrivere la query sostituendo i valori ipa_id per il nome utente nella clausola where. Per me questo è equivalente alla query originale. MySQL lo vede diversamente. Se faccio questo, l'ottimizzatore fa uso dell'indice sul grande tavolo.
mysql> spiegare
SELEZIONA master_final.PayorCode, sum (master_final.Mbrct) AS MbrCt
DA master_final
INNER JOIN IPA ON ipa.ipa_code = master_final.IPA
INNER JOIN assign_ipa ON ipa.ipa_id = assign_ipa.ipa_id
* WHERE assign_ipa.ipa_id in ('688', '689') *
GROUP BY master_final.PayorCode, master_final.ipa \ G;
* ** * *** *** *** 1.fila * *** *** *** ***
id: 1
select_type: SEMPLICE
tabella: IPA
Tipo: gamma
possible_keys: PRIMARY, ipa_code
chiave: PRIMARY
key_len: 4
ref: NULL
righe: 2
Extra: Utilizzo di dove; Uso temporaneo; Utilizzando filesort
* ** * ** * ** * ** * 2. riga * ** * ** * ** * ** *
id: 1
select_type: SEMPLICE 01.238.007,316 mila tabella: assign_ipa
Tipo: rif
possible_keys: ipa_id
chiave: ipa_id
key_len: 4
ref: wc_test.ipa.ipa_id
righe: 37
Extra: Usando dove
* ** * ** * ** * ** * 3. riga * *** *** *** ***
id: 1
select_type: SEMPLICE
tabella: master_final
Tipo: ref
possibili_keys: idx_IPA
chiave: idx_IPA
key_len: 5
ref: wc_test.ipa.ipa_code
righe:
Extra: Usando dove
3 rows in set (0.00 sec)
L'unica cosa che ho cambiato è una clausola in cui tale non colpisce nemmeno direttamente il grande tavolo. Eppure, l'ottimizzatore utilizza l'indice 'idx_IPA' sulla grande tabella e la scansione completa della tabella non viene più utilizzata. La query quando riscritta in questo modo è molto veloce.
OK, è molto di fondo. Ora la mia domanda. Perché la clausola where dovrebbe avere importanza per l'ottimizzatore? In entrambi i casi la clausola restituirà lo stesso set di risultati dalla tabella più piccola, eppure ottengo risultati drammaticamente diversi a seconda di quale uso. Ovviamente, voglio usare la clausola where che contiene il nome utente piuttosto che provare a passare tutti gli ID associati alla query. Come scritto però, questo non è possibile?
- Qualcuno può spiegare perché questo sta accadendo?
- Come posso riscrivere la mia query per evitare la scansione completa della tabella?
Grazie per avermi seguito. So che è una domanda molto lunga.
Ho letto un articolo di uno degli sviluppatori MySQL (qualche tempo fa) che l'ottimizzatore era ancora in lavorazione - e quindi sono stati assorbiti da Oracle. Hai provato a utilizzare "suggerimenti" o forse spostando "assign_ipa.username = 'SJones'" nel JOIN? –