Tengo una consulta que me está dando problemas y no puedo entender por qué el optimizador de consultas de MySQL se está comportando como está. Aquí está la información de fondo:¿Por qué MySQL no puede optimizar esta consulta?
Tengo 3 tablas. Dos son relativamente pequeños y uno es grande.
Tabla 1 (muy pequeña, 727 filas):
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
),
CLAVEipa_code
(ipa_code
)) ENGINE = MyISAM
la Tabla 2 (más bien pequeñas, 59455 filas):
CREATE TABLE
assign_ipa
(
assignid
int (11) NOT NULL AUTO_INCREMENT,
ipa_id
int (11) NOT NULL,
userid
int (11) NOT NUL L,
username
varchar (20) NULL DEFAULT,
compid
int (11) NULL DEFAULT,
PayorCode
char (10) NULL DEFAULT
PRIMARY KEY (assignid
),
UNIQUE KEYassignid
(assignid
,ipa_id
),
CLAVEipa_id
(ipa_id
)
) ENGINE = MyISAM
Tabla 3 (grande, 24,711,730 filas):
crear la tabla
master_final
(
IPA
int (11) NULL DEFAULT,
MbrCt
smallint (6) DEFAULT '0',
PayorCode
varchar (4) DEFAULT 'WC',
CLAVEidx_IPA
(IPA
)
) ENGINE = MyISAM DEFAULT
Ahora para la consulta. Estoy haciendo una combinación de tres vías utilizando las dos primeras tablas más pequeñas para esencialmente subconjunto de la gran tabla en uno de sus valores indexados. Básicamente, obtengo una lista de ID para un usuario, SJOnes y consulto el archivo grande para esos ID.
mysql> explicar
master_final SELECT.PayorCode, sum (master_final.Mbrct) AS MbrCt
DE master_final
INNER JOIN ipa EN ipa.ipa_code = master_final.IPA
combinación interna assign_ipa EN ipa.ipa_id = assign_ipa.ipa_id
WHERE assign_ipa.username = 'SJones'
GROUP BY master_final.PayorCode, master_final.ipa \ G;
* ** * ** * ** * ** * 1. fila * ** * ** * ** * * * *
id: 1
SELECT_TYPE: SIMPLE
tabla: master_final
Tipo: Todos
possible_keys: idx_IPA
importante: NULL
key_len: NULL
ref: NULL
filas:
adicional: El uso temporal; Usando filesort
* ** * ** * ** * ** * 2. fila * ** * ** * ** * ** *
id: 1
SELECT_TYPE: SIMPLE
tabla: IPA
Tipo: ref
possible_keys: PRIMARIA, ipa_code
clave: ipa_code
key_len: 5
ref: wc_test.master_final.IPA
filas: 1
adicional: Usando donde
* ** * ** * ** * ** * 3.fila * ** * ** * ** * ** *
id: 1
SELECT_TYPE: SIMPLE
tabla: assign_ipa
Tipo: ref
possible_keys: ipa_id
clave: ipa_id
key_len: 4
ref: wc_test.ipa .ipa_id
filas: 37
adicionales: (! como 30 minutos), utilizando, cuando
3 rows in set (0.00 sec)
Esta consulta se lleva para siempre. El enunciado de explicación me dice por qué, está haciendo un escaneo de tabla completo en la gran mesa, a pesar de que hay un índice perfectamente bueno. No lo está usando. No entiendo esto. Puedo ver la consulta y ver que solo necesita consultar un par de ID de la gran tabla. Si puedo hacerlo, ¿por qué el optimizador de MySQL no puede hacerlo?
Para ilustrar, aquí están las IDs asociados con '' SJones:
mysql> select nombre de usuario, ipa_id de assign_ipa donde nombre de usuario = SJones ';
+ ---------- + -------- +
| nombre de usuario | ipa_id |
+ ---------- + -------- +
| SJones | 688 |
| SJones | 689 |
+ ---------- + -------- +
2 filas en el conjunto (0,02 seg)
Ahora, puedo reescribir la consulta sustituyendo los valores ipa_id para el nombre de usuario en la cláusula where. Para mí esto es equivalente a la consulta original. MySQL lo ve de manera diferente. Si hago esto, el optimizador hace uso del índice en la gran mesa.
mysql> SELECT explicar
master_final.PayorCode, suma (master_final.Mbrct) AS MbrCt
DE master_final
INNER JOIN IPA EN ipa.ipa_code = master_final.IPA
INNER JOIN assign_ipa EN 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: SIMPLE
tabla: ipa
tipo: gama
possible_keys: PRIMARIO, ipa_code
clave: PRIMARIA
key_len: 4
ref: NULL
filas: 2
Extra: Uso de; Usando temporal; Usando filesort
* ** * ** * ** * ** * 2. fila * ** * ** * ** * ** *
id: 1
SELECT_TYPE: SIMPLE
tabla: assign_ipa
Tipo: ref
possible_keys: ipa_id
clave: ipa_id
key_len: 4
ref: wc_test.ipa.ipa_id
filas: 37
adicional: Usando donde
* ** * ** * ** * ** * 3. fila * ** * ** * ** * ** *
id: 1
SELECT_TYPE: SIMPLE
tabla: master_final
Tipo: ref
possible_keys: idx_IPA
clave: idx_IPA
key_len: 5
ref: wc_test.ipa.ipa_code
filas:
adicional: Usando donde
3 rows in set (0.00 sec)
Lo único que he cambiado es que una cláusula donde ni siquiera llega directamente a la gran mesa. Y aún así, el optimizador usa el índice 'idx_IPA' en la gran tabla y ya no se usa la exploración de tabla completa. La consulta cuando se vuelve a escribir de esta manera es muy rápida.
OK, eso es mucha historia. Ahora mi pregunta. ¿Por qué debería importar la cláusula where al optimizador? Cualquiera de las cláusulas devolverá el mismo conjunto de resultados de la tabla más pequeña, y sin embargo obtengo resultados dramáticamente diferentes dependiendo de cuál use. Obviamente, quiero usar la cláusula where que contiene el nombre de usuario en lugar de intentar pasar todos los ID asociados a la consulta. Como está escrito, ¿no es posible?
- ¿Alguien puede explicar por qué sucede esto?
- ¿Cómo podría reescribir mi consulta para evitar el escaneo completo de la tabla?
Gracias por seguir conmigo. Sé que es una pregunta muy larga.
Leí un artículo de uno de los desarrolladores de MySQL (hace algún tiempo) que el optimizador todavía estaba en proceso, y luego fueron absorbidos por Oracle. ¿Has intentado utilizar "pistas" o tal vez mover el "assign_ipa.username = 'SJones'" a JOIN? –