Tengo una base de datos sqlite con dos tablas, cada una con 50,000 filas, que contiene nombres de personas (falsas). He construido una consulta simple para averiguar cuántos nombres hay (nombre de pila, inicial del segundo nombre, apellido) que son comunes a ambas tablas:
select count(*) from fakenames_uk inner join fakenames_usa on fakenames_uk.givenname=fakenames_usa.givenname and fakenames_uk.surname=fakenames_usa.surname and fakenames_uk.middleinitial=fakenames_usa.middleinitial;
Cuando no hay índices excepto en las claves primarias (irrelevantes para esta consulta), se ejecuta rápidamente:
[james@marlon Downloads] $ time sqlite3 generic_data_no_indexes.sqlite "select count(*) from fakenames_uk inner join fakenames_usa on fakenames_uk.givenname=fakenames_usa.givenname and fakenames_uk.surname=fakenames_usa.surname and fakenames_uk.middleinitial=fakenames_usa.middleinitial;"
131
real 0m0.115s
user 0m0.111s
sys 0m0.004s
Pero si agrego índices a las tres columnas en cada tabla (seis índices en total):
CREATE INDEX `idx_uk_givenname` ON `fakenames_uk` (`givenname` )
//etc.
entonces corre dolorosamente lento:
[james@marlon Downloads] $ time sqlite3 generic_data.sqlite "select count(*) from fakenames_uk inner join fakenames_usa on fakenames_uk.givenname=fakenames_usa.givenname and fakenames_uk.surname=fakenames_usa.surname and fakenames_uk.middleinitial=fakenames_usa.middleinitial;"
131
real 1m43.102s
user 0m52.397s
sys 0m50.696s
¿Hay alguna rima o razón para esto?
Aquí está el resultado de EXPLAIN QUERY PLAN
la versión sin índices:
0|0|0|SCAN TABLE fakenames_uk
0|1|1|SEARCH TABLE fakenames_usa USING AUTOMATIC COVERING INDEX (middleinitial=? AND surname=? AND givenname=?)
Esto es con índices:
0|0|0|SCAN TABLE fakenames_uk
0|1|1|SEARCH TABLE fakenames_usa USING INDEX idx_us_middleinitial (middleinitial=?)
performance
index
optimization
sqlite
count
seguridad quiástica
fuente
fuente
middleinitial
,surname
ygivenname
)?SELECT c FROM t WHERE a=1 AND b=2
, el índicet(a,b,c)
está cubriendo perot(a,b)
no. El beneficio de los índices de cobertura es que todo el resultado de la consulta se puede extraer directamente del índice, mientras que los índices de no cobertura encuentran rápidamente las filas relevantes, pero aún necesita referirse a los datos de la tabla principal para seleccionar los valores.Respuestas:
En SQLite, las uniones se ejecutan como uniones de bucle anidadas, es decir, la base de datos pasa por una tabla y, para cada fila, busca filas coincidentes de la otra tabla.
Si hay un índice, la base de datos puede buscar rápidamente cualquier coincidencia en el índice y luego ir a la fila de la tabla correspondiente para obtener los valores de cualquier otra columna que sea necesaria.
En este caso, hay tres índices posibles. Sin ninguna información estadística (que se crearía ejecutando ANALYZE ), la base de datos elige la más pequeña para reducir las E / S. Sin embargo, el
middleinitial
índice es inútil porque no reduce en gran medida el número de filas de la tabla que deben recuperarse; y el paso adicional a través del índice en realidad aumenta la E / S necesaria porque las filas de la tabla ya no se leen en orden, sino al azar.Si no hay índice, la búsqueda de filas coincidentes requeriría una exploración completa de la tabla de la segunda tabla para cada fila de la primera tabla. Esto sería tan malo que la base de datos estima que vale la pena crear y luego descartar un índice temporal solo para esta consulta. Este índice temporal ("AUTOMÁTICO") se crea en todas las columnas utilizadas para la búsqueda. La operación COUNT (*) no necesita valores de ninguna otra columna, por lo que este índice es un índice de cobertura , lo que significa que no es necesario buscar la fila de la tabla correspondiente a una entrada de índice, lo que ahorra aún más I / O.
Para acelerar esta consulta, cree este índice de forma permanente, de modo que ya no sea necesario construir uno temporal:
El índice
surname
activado ya no es necesario porque el índice de tres columnas se puede usar para cualquier búsqueda en esta columna.El índice
givenname
puede ser útil si solo realiza búsquedas en esta columna.El índice en
middleinitial
siempre no tiene valor: una consulta que busca uno de los 26 valores posibles es más rápida si solo escanea toda la tabla.fuente