2011-10-25 15 views
10

Tengo una consulta que contiene una subconsulta que siempre causa una EXPLORACIÓN de una tabla muy grande que da como resultado tiempos de consulta deficientes.¿Por qué una subconsulta causa un escaneo cuando una lista estática no lo hace?

Esta es la consulta que estoy usando:

SELECT PersonId 
    FROM person 
WHERE PersonId IN (
        SELECT PersonId 
         FROM relationship 
        WHERE RelatedToPersonId = 12270351721 
        ); 

El plan de consulta se informa como:

SCAN TABLE person (~100000 rows) 
EXECUTE LIST SUBQUERY 1 
SEARCH TABLE relationship USING INDEX relationship_RelatedToPersonId_IDX (RelatedToPersonId=?) (~10 rows) 

La misma consulta con una lista estática (equivalente a los resultados de la subconsulta) :

SELECT PersonId 
    FROM person 
WHERE PersonId IN (12270351727,12270351730,12270367969,12387741400); 

Y el plan de consulta para que:

SEARCH TABLE person USING COVERING INDEX sqlite_autoindex_person_1 (PersonId=?) (~5 rows) 
EXECUTE LIST SUBQUERY 1 

¿Por qué la primera consulta solicita un escaneo si el segundo no?

+0

Si el conjunto de datos de su sub consulta es pequeño, entonces una mesa transitoria puede trabajar. Algo así como: Seleccione Personid From (SELECCIONE la relación PersonId FROM WHERE RelatedToPersonId = 12270351721); – DallinDyer

+0

Probé una tabla transitoria y todavía produjo un escaneo. – goto10

+0

¿Puede existir un 'PersonId' en' relationship' si no hay una fila en 'person'? Si no, ¿la subconsulta (posiblemente con un 'distinct') no satisface toda la consulta? –

Respuesta

2

La tabla debe escanearse porque está incluyendo otro campo (RelatedToPersonId) en la cláusula WHERE de su subconsulta. La lista de PersonID puede ir directamente al índice.

+0

RelatedToPersonId está en la tabla de relaciones. ¿Por qué eso causaría un escaneo de la mesa de la persona? ¿No debería esa subconsulta ejecutarse de forma independiente, producir su lista de resultados y luego comportarse de la misma manera que la consulta con la lista estática de valores? Si nos fijamos en el plan de consulta para esa consulta, la parte de la subconsulta realmente está llegando al índice. – goto10

+0

El optimizador de SQLite definitivamente debe poder saber que la subconsulta puede usar un índice y asegurarse de ejecutarlo primero y luego restringir las filas de la tabla de personas a ese conjunto de resultados. – jjxtra

0

Este:

SELECT PersonId 
    FROM person 
WHERE PersonId IN (12270351727,12270351730,12270367969,12387741400); 

no es más que el azúcar sintáctica para esto:

SELECT PersonId 
    FROM person 
WHERE (
     PersonId = 12270351727 
     OR PersonId = 12270351730 
     OR PersonId = 12270367969 
     OR PersonId = 12387741400 
     ); 
+0

Sí, pero eso no explica por qué es necesario un escaneo cuando se emplea una subconsulta o, lo que es más importante, cómo evitar ese escaneo. Simplemente parece que el optimizador de consultas SQLite no está produciendo un plan de consulta óptimo en este caso. – goto10

1

No puedo reproducir. Ejecuté las siguientes declaraciones en una versión fresca SQLite 3.7.8 instalar:

create table person (id int not null primary key, name varchar(20)); 
insert into person values (1, 'a'); 
insert into person select id + 1, name || 'b' from person; 
insert into person select id + 2, name || 'c' from person; 
insert into person select id + 4, name || 'd' from person; 
insert into person select id + 8, name || 'e' from person; 
insert into person select id + 16, name || 'f' from person; 
insert into person select id + 32, name || 'g' from person; 
insert into person select id + 64, name || 'h' from person; 
insert into person select id + 128, name || 'i' from person; 
insert into person select id + 256, name || 'j' from person; 
insert into person select id + 512, name || 'k' from person; 
insert into person select id + 512, name || 'l' from person; 
insert into person select id + 1024, name || 'l' from person; 
insert into person select id + 2048, name || 'm' from person; 
insert into person select id + 4096, name || 'n' from person; 
insert into person select id + 8192, name || 'o' from person; 
insert into person select id + 16384, name || 'p' from person; 
insert into person select id + 32768, name || 'q' from person; 
insert into person select id + 65536, name || 'r' from person; 
select count(*) from person; 

create table relation (id int, related int); 
insert into relation select id, id + 1 from person; 
insert into relation select id, id + 2 from person; 
insert into relation select id, id + 3 from person; 
insert into relation select id, id + 4 from person; 
insert into relation select id, id + 5 from person; 
insert into relation select id, id + 6 from person; 
insert into relation select id, id + 7 from person; 
insert into relation select id, id + 8 from person; 
insert into relation select id, id + 9 from person; 
insert into relation select id, id + 10 from person; 
delete from relation where related not in (select id from person); 

create index relatedToPerson on relation(related); 
explain query plan select id from person 
    where id in (select id from relation where related = 2345); 

Los resultados para la declaración plan de consulta:

0|0|0|SEARCH TABLE person USING COVERING INDEX sqlite_autoindex_person_1 (id=?)(~25 rows) 
0|0|0|EXECUTE LIST SUBQUERY 1 
1|0|0|SEARCH TABLE relation USING INDEX relatedToPerson (related=?) (~10 rows) 

¿por qué no trabajar para usted? Las razones que se me ocurren:

  • su relación de tabla no contiene la columna PERSONID (por favor, compruebe )
  • está utilizando otra versión de SQLite
  • tiene otras restricciones, por ejemplo índices únicos .

¿Puedes ejecutar el script anterior y verificar si obtienes los mismos resultados que obtuve?

+0

He estado demasiado ocupado esta semana para llegar a esto y me voy a ir de la ciudad mañana, así que tal vez no pueda darle más tiempo a esto por unos días más. Pero no quiero desperdiciar la recompensa, así que te lo adjudico por el esfuerzo que has hecho. Si tu ejemplo realmente no causa ningún escaneo, entonces eso me dará un buen lugar desde el que comenzar a tratar de entender por qué. el mío no funciona igual. – goto10

+0

Buena suerte. Por favor publique sus resultados aquí. Tengo curiosidad por saber cuál es la diferencia. – boes

+0

Tipos de datos no coincidentes (texto frente a entero) – DallinDyer

1

No estoy seguro de por qué hay una exploración de tabla. Pero no hay necesidad de utilizar una subselección aquí: También es posible usar una combinación:

SELECT person.PersonId 
    FROM person JOIN relationship ON person.PersonId = relationship.PersonId 
WHERE RelatedToPersonId = 12270351721; 

No estoy seguro si SQLite apoya esta sintaxis JOIN, pero esto podría también ser re-escrito usando ya dónde:

SELECT person.PersonId 
    FROM person, relationship 
WHERE person.PersonId = relationship.PersonId 
    AND RelatedToPersonId = 12270351721; 

Y, tal vez esto es más lo que el optimizador está ajustado.

+0

Como mencioné en los comentarios anteriores, esta es una consulta artificial que muestra un caso muy simple de un análisis inesperado. La consulta de "vida real" es sustancialmente más compleja pero se reduce a la misma situación: una subconsulta que produce una lista breve que produce un escaneo mientras que poner los mismos valores en una lista estática no causa el escaneo. – goto10

2

Probablemente los tipos de datos no coincidentes. Does person.PersonId y relationship.PersonId tienen la misma definición de datos.

+1

Derecho encendido. ¡Gracias! – DallinDyer

0

Es causa de barrido completo debido a sub-consulta ejecutado para cada fila de person. Las subconsultas no "almacenan en caché" su conjunto de resultados para convertirse en IN.

Espero que ayude.

Cuestiones relacionadas