14

Aquí está la consulta:¿Por qué es esto una exploración de índice y no una búsqueda de índice?

SELECT  top 100 a.LocationId, b.SearchQuery, b.SearchRank 
FROM  dbo.Locations a 
INNER JOIN dbo.LocationCache b ON a.LocationId = b.LocationId 
WHERE  a.CountryId = 2 
AND   a.Type = 7 

Ubicación: Índices

PK_Locations:

LocationID

IX_Locations_CountryId_Type:

CountryId, Tipo

índices LocationCache:

PK_LocationCache:

LocationID

IX_LocationCache_LocationId_SearchQuery_SearchRank:

LocationID, SearchQuery, SearchRank

plan de ejecución:

enter image description here

Por lo tanto, está haciendo un Índice Seek el enlace direcciones, utilizando el índice de cobertura, fresco.

¿Pero por qué está haciendo un Análisis de índice en el índice de cobertura de LocationCache?

Ese índice de cobertura tiene LocationId, SearchQuery, SearchRank en el índice (no como "Columnas incluidas").

Hover en el recorrido de índice:

enter image description here

Esta consulta tiene que ir en una vista indizada servida por un catálogo de SQL Server FTS, consumida por un plugin autocompletar, por lo que tiene que ser 100% optimizado .

En el momento en que la consulta anterior está tardando 3 segundos. Debe ser < 0.

¿Alguna idea?

+1

Probablemente no relacionados pero tengo curiosidad, ¿por qué no tienen una orden por la parte superior, mientras que el uso de '100' –

+1

Fuera de interés (pero no pretende ser una solución de cualquier tipo) hace cambiar el 'INNER JOIN' para' INNER LOOP JOIN' acelerar las cosas o ralentizar las cosas? –

+0

¿Están sus llaves principales agrupadas por casualidad? – JStead

Respuesta

8

Mientras que teniendo en cuenta que el resultado será una consulta que puede realizar mal como y cuando se realizan cambios adicionales a la misma, utilizando un INNER LOOP JOIN debería forzar el índice de cobertura a ser utilizado en dbo.LocationCache.

SELECT  top 100 a.LocationId, b.SearchQuery, b.SearchRank 
FROM  dbo.Locations a 
INNER LOOP JOIN dbo.LocationCache b ON a.LocationId = b.LocationId 
WHERE  a.CountryId = 2 
AND   a.Type = 7 
+1

Resulta que no puedo usarlo de todos modos, ya que necesita ir a una vista indexada (porque los catálogos FTS necesitan eso) pero las vistas indizadas con PK agrupado no pueden tener pistas. :( – RPM1984

+1

Otro gatito acaba de morir: ~ ( –

+0

@ Pure.Krome - Espero que haya sido una intención malvada de gatito en la dominación mundial. –

4

¿Has intentado actualizar tus estadísticas?

UPDATE STATISTICS dbo.LocationCache 

Aquí hay un par de buenas referencias en lo que hace y por qué el optimizador de consultas elegirá una exploración sobre un buscamos.

http://social.msdn.microsoft.com/Forums/en-CA/sqldatabaseengine/thread/82f49db8-0c77-4bce-b26c-1ad0a4af693b

Resumen

Hay varias cosas a tener en cuenta aquí. En primer lugar, cuando SQL decide cuál es el mejor (lo suficientemente bueno) plan para usar, mira la consulta, y luego también mira las estadísticas que almacena sobre las tablas involucradas.

A continuación, decide si es más eficiente de buscar por el índice o escanear todo el nivel hoja del índice (en este caso, se trata de tocar cada página en la tabla, ya que es una índice agrupado) Hace esto por mirando un número de cosas. En primer lugar, adivina cuántas filas/páginas de necesitará escanear. Este se llama el punto de inflexión, y es un porcentaje menor de del que pueda pensar. Ver este gran blog Kimberly Tripp http://www.sqlskills.com/BLOGS/KIMBERLY/category/The-Tipping-Point.aspx

Si está dentro de los límites establecidos para el punto de inflexión , puede ser porque sus estadísticas no están actualizadas, o su índice de es muy fragmentado.

Es posible forzar SQL para buscar un índice mediante el uso de la consulta FORCESEEK pista, pero por favor utilice esto con cautela, ya que por lo general, que le proporciona mantener todo weel mantiene, SQL es bastante bueno en decidir qué el plan más eficiente de será !!

+0

sí, lo intenté, como en los comentarios anteriores. – RPM1984

+0

@ RPM1984 No había notado que había 13 comentarios más – cordsen

+0

+1 para el enlace a las excelentes publicaciones del blog de Kimberly Tripp –

29

Está utilizando una exploración de índice principalmente porque también está utilizando una combinación de fusión. El operador Combinar combinación requiere dos flujos de entrada que están ordenados en un orden que es compatible con las condiciones de Unión.

Y está utilizando el operador Merge Join para realizar su INNER JOIN porque cree que será más rápido que el operador de unión de bucle anidado más típico. Y probablemente sea correcto (generalmente lo es), al usar los dos índices que ha elegido, tiene flujos de entrada que están ordenados previamente según su condición de unión (LocationID). Cuando los flujos de entrada se clasifican previamente de esta manera, las uniones combinadas son casi siempre más rápidas que las otras dos (uniones en bucle y hash).

Lo malo es lo que ha notado: parece que está escaneando todo el índice, entonces, ¿cómo puede ser más rápido si está leyendo tantos registros que quizás nunca se utilicen? La respuesta es que los escaneos (debido a su naturaleza secuencial) pueden leer de 10 a 100 veces más registros por segundo que búsquedas.

Ahora las búsquedas generalmente ganan porque son selectivas: solo obtienen las filas que usted solicita, mientras que las exploraciones no son selectivas: deben devolver cada fila en el rango.Pero debido a que los escaneos tienen un mucho mayor tasa de lectura, con frecuencia pueden vencer a las búsquedas siempre que la relación de Filas descartadas a Filas coincidentes sea inferior que la relación de Filas de escaneo/seg VS. Busca filas/seg.

Preguntas?


bien, me han pedido que explique la última frase más:

A "desechados Fila" es uno que el Scan lee (porque tiene que leer todo en el índice), pero eso será rechazado por el operador Combinar combinación, porque no tiene una coincidencia en el otro lado, posiblemente porque la condición de la cláusula WHERE ya lo ha excluido.

"Filas coincidentes" son las que se leen que coinciden con algo en la Combinación de combinación. Estas son las mismas filas que una Seek habría leído si el escaneo hubiera sido reemplazado por una búsqueda.

Puede averiguar lo que hay mirando las estadísticas en el Plan de consulta. ¿Ves esa enorme y gruesa flecha a la izquierda del Index Scan? Eso representa cuántas filas cree que el optimizador leerá con el Escaneo. El cuadro de estadísticas del Análisis de índice que ha publicado muestra que las filas reales devueltas son aproximadamente 5,4 millones (5 394 402). Esto es igual a:

TotalScanRows = (MatchingRows + DiscardedRows) 

(En mis términos, de todos modos). Para obtener las Filas coincidentes, consulte las "Filas reales" informadas por el operador Fusionar combinación (puede que tenga que quitar las 100 principales para obtener esto con precisión). Una vez que sepa esto, puede obtener las filas descartadas por:

DiscardedRows = (TotalScanRows - MatchingRows) 

Y ahora puede calcular la relación.

+0

mente == soplado (de una mala manera). Preguntas "sip citación:" la proporción de Filas Descartadas a Filas coincidentes es menor que la relación Filas de escaneo/seg VS Filas de búsqueda/seg. "<- ¿pueden explicar esto ... y CÓMO? podemos usar esto para verificar esta estadística, etc. –

+1

+1 esta es una respuesta extremadamente útil. Después de haber mirado mis números, está haciendo un _Esquema de índice_ en 400,000 filas pero solo coincide con 201 en un _Agregar unir_. Por lo tanto, supongo que un _Loop Join_ estaría lejos más eficiente basado en las proporciones. –

+0

tu publicación parece extremadamente significativa. ¿Puede por favor guiarme, cómo calcular la proporción ** Escanear filas/seg ** frente a ** Buscar filas/seg ** –

0

Hice una prueba rápida y se le ocurrió la siguiente

CREATE TABLE #Locations 
(LocationID INT NOT NULL , 
CountryID INT NOT NULL , 
[Type] INT NOT NULL 
CONSTRAINT PK_Locations 
     PRIMARY KEY CLUSTERED (LocationID ASC) 
) 

CREATE NONCLUSTERED INDEX [LocationsIndex01] ON #Locations 
(
    CountryID ASC, 
    [Type] ASC 
) 

CREATE TABLE #LocationCache 
(LocationID INT NOT NULL , 
SearchQuery VARCHAR(50) NULL , 
SearchRank INT NOT NULL 
CONSTRAINT PK_LocationCache 
     PRIMARY KEY CLUSTERED (LocationID ASC) 

) 

CREATE NONCLUSTERED INDEX [LocationCacheIndex01] ON #LocationCache 
(
    LocationID ASC, 
    SearchQuery ASC, 
    SearchRank ASC 
) 

INSERT INTO #Locations 
SELECT 1,1,1 UNION 
SELECT 2,1,4 UNION 
SELECT 3,2,7 UNION 
SELECT 4,2,7 UNION 
SELECT 5,1,1 UNION 
SELECT 6,1,4 UNION 
SELECT 7,2,7 UNION 
SELECT 8,2,7 --UNION 

INSERT INTO #LocationCache 
SELECT 4,'BlahA',10 UNION 
SELECT 3,'BlahB',9 UNION 
SELECT 2,'BlahC',8 UNION 
SELECT 1,'BlahD',7 UNION 
SELECT 8,'BlahE',6 UNION 
SELECT 7,'BlahF',5 UNION 
SELECT 6,'BlahG',4 UNION 
SELECT 5,'BlahH',3 --UNION 

SELECT * FROM #Locations 
SELECT * FROM #LocationCache 

SELECT  top 3 a.LocationId, b.SearchQuery, b.SearchRank 
FROM  #Locations a 
INNER JOIN #LocationCache b ON a.LocationId = b.LocationId 
WHERE  a.CountryId = 2 
AND   a.[Type] = 7 

DROP TABLE #Locations 
DROP TABLE #LocationCache 

Para mí, el plan de consulta muestra que busca con un bucle anidado interior. Si ejecuta esto, ¿obtiene ambas búsquedas? Si lo hace, haga una prueba en su sistema y cree una copia de su tabla de Ubicaciones y UbicaciónCache y llámeles, por ejemplo, Locations2 y LocationCache2 con todos los índices y copie sus datos en ellos. Entonces prueba tu consulta golpeando las nuevas tablas?

+3

Es probablemente porque no tiene 5.4M filas en sus tablas. – RBarryYoung

+0

lol nice, la razón por la que menciono probar nuevas mesas es que tuve un problema similar hace un tiempo en dos mesas con más de 34 millones de filas, nada de lo que hice lo hizo funcionar como debería. Pero en un servidor de prueba dudoso funcionó bien y rápido. Después de crear nuevas tablas y copiar los datos, todo estaba feliz. Todavía no tengo idea de qué fue lo que causó el problema, tal vez las estadísticas no lo sé. –

+0

Lo que es frustrante ... es cuando tengo el mismo problema ... y uso las ESTADÍSTICAS DE ACTUALIZACIÓN ... no estoy seguro de que ayude :( –

2

En resumen: no tiene filtro en LocationCache, se debe devolver todo el contenido de la tabla. Tienes un índice completo de cobertura. Index SCAN (una vez) es la operación más económica, y el optimizador de consultas la selecciona.

Para optimizar: Se está uniendo a las tablas completas, y luego solo obtiene los mejores 100 resultados. No sé qué tan grandes son, pero intente subconsultar la tabla [Ubicaciones] CountryId, Type y luego únase solo al resultado con [LocationCache]. Será más rápido si tienes más de 1000 filas allí. Además, intente agregar algunos filtros más restrictivos antes de las uniones si es posible.

Escaneo de índice: Como un escaneo toca cada fila de la tabla, califique o no, el costo es proporcional al número total de filas en la tabla. Por lo tanto, una exploración es una estrategia eficiente si la tabla es pequeña o si la mayoría de las filas califican para el predicado. Búsqueda de índice: Dado que una búsqueda solo toca las filas que califican y las páginas que contienen estas filas calificativas, el costo es proporcional al número de filas y páginas calificadas en lugar de a la cantidad total de filas en la tabla.

Si hay un índice en una tabla, y si la consulta toca una mayor cantidad de datos, lo que significa que la consulta está recuperando más del 50 por ciento o 90 por ciento de los datos, y luego el optimizador escanearía todos páginas de datos para recuperar las filas de datos.

source

Cuestiones relacionadas