Orden de campo en un orden de índice compuesto con campos de alta selectividad y baja selectividad


11

Tengo una tabla de SQL Server con más de 3 mil millones de filas. Una de mis consultas lleva mucho tiempo, así que estoy considerando optimizarla. La consulta se ve así:

SELECT [Enroll_Date]
      ,Count(*) AS [Record #]
      ,Count(Distinct UserID) AS [User #]
  FROM UserTable
  GROUP BY [Enroll_Date]

[Enroll_Date] es una columna de baja selectividad con menos de 50 valores posibles, mientras que la columna UserID es una columna de alta selectividad con más de 200 millones de valores distintos. Basado en mi investigación, creo que debería crear un índice compuesto no agrupado en estas dos columnas, y en teoría la columna de alta selectividad debería ser la primera columna. Pero no estoy seguro en mi caso, ¿funcionaría porque estoy usando la columna de baja selectividad en el grupo por cláusula?

Esta tabla no tiene índice agrupado.


¿Puede publicar el plan de ejecución real xml (use pastebin y vincúlelo aquí)? ¿Qué versión del servidor SQL está utilizando?
Kin Shah

3
El índice con la columna altamente selectiva primero será inútil para la consulta específica.
ypercubeᵀᴹ

Es una buena práctica utilizar la columna de mayor selectividad como la primera columna clave en un índice (normalmente). En este escenario, como has adivinado, no te ayuda en absoluto. ¡Es posible que necesite dos índices! ¿Qué sucede cuando usa enrol_date primero y user_id segundo?
paulbarbin

Respuestas:


12

Como alternativa a la solución de @ AaronBertrand (si no puede o no desea crear una vista indizada), le recomendaría que cree un índice en (Enroll_Date, UserID). Si este tipo de pregunta es muy común en su tabla, probablemente este debería ser su índice agrupado.

En general, no recomendaría índices de alta selectividad como una "mejor práctica" general, sino que mire qué índice le dará a su consulta el mejor rendimiento.

Un índice activado (Enroll_Date, UserID)le dará a su consulta un plan de consulta altamente optimizado y sin bloqueo con Stream Aggregates.

Plan de consulta agregada de flujo

"Sin bloqueo" en este contexto significa que la consulta no necesita almacenar cantidades significativas de datos (como, por ejemplo, una ordenación o un agregado hash lo haría), lo que significa que (a) comienza a devolver filas inmediatamente, y ( b) prácticamente no consume memoria de trabajo.


Divertido, con 4 segundos de diferencia y la misma respuesta.
usr

11

La respuesta de Aarons es una gran solución. Contestaré la pregunta asumiendo que no quieres tomar ese enfoque.

La consulta que publicaste generalmente se ejecutará primero agrupando (Enroll_Date, UserID)y luego nuevamente (Enroll_Date). Esta optimización es nueva para SQL Server 2012. Tiene efecto en caso de una sola COUNT DISTINCT.

Un índice en esas dos columnas en el orden específico (Enroll_Date, UserID)será suficiente para obtener un plan eficiente que canalice una exploración de índice en dos agregados de flujo consecutivos. El orden opuesto no permitiría ese plan.

Por lo tanto, use la orden (Enroll_Date, UserID). No tienes elección aquí.


5 segundos de diferencia y la misma solución. Bien jugado, señor. :)
Daniel Hutmacher

@DanielHutmacher OMG, ¿lograremos igualar nuestras publicaciones por tercera vez? +1 para ti! ¿Cómo podría no upvote una respuesta idéntica?
usr

Glitch en la matriz. :)
Daniel Hutmacher

Muchas gracias. Estoy creando el índice y publicaré la mejora después de que haya terminado. La versión del servidor es Microsoft SQL Server 2008 R2 en AWS, pero supongo que sigue siendo la única opción independientemente.
Thinkinger

@Thinkinger en caso de que no estés aceptando el enfoque de Aarons, tienes una difícil elección :)
usr

11

Suena como un escenario ideal para una vista indizada, que le permite pagar los cálculos y los agregados en el momento de la escritura en lugar del tiempo de consulta.

CREATE VIEW dbo.MyIndexedView
WITH SCHEMABINDING
AS 
  SELECT Enroll_Date, UserID, RawCount = COUNT_BIG(*)
  FROM dbo.UserTable
  GROUP BY Enroll_Date, UserID;
GO

CREATE UNIQUE CLUSTERED INDEX CIX_miv ON dbo.MyIndexedView(Enroll_Date, UserID);

Eso tomará un tiempo en crearse y, por supuesto, requerirá mantenimiento en todas las operaciones DML, al igual que un índice en la tabla base.

Ahora, la consulta en esta vista sería bastante similar: cada fila de la vista ahora representa un combo de usuario / fecha distinto, por lo que esa cifra se puede calcular con un solo COUNT (*), mientras que el número total de filas en la tabla base es ya está parcialmente agregado para usted, ahora solo necesita agregarlos usando SUMA por fecha:

SELECT Enroll_Date, 
  [Record #] = SUM(RawCount),
  [User #] = COUNT(*)
FROM dbo.MyIndexedView WITH (NOEXPAND)
GROUP BY Enroll_Date; 

Sugerencia NOEXPAND agregada, después de recordar esto y esto .

Puedo decirle sin lugar a dudas que esta consulta será más rápida que su consulta actual (pero no por cuánto), excepto en el raro caso en que tenga exactamente un usuario para cada fecha (en cuyo caso la misma cantidad de datos tendrá para leer) y las columnas que conocemos son las únicas columnas en el índice de la tabla base. Si ese aumento de rendimiento en el momento de la lectura vale la pena el trabajo extra que afectará la parte de escritura de su carga de trabajo es algo que no podemos decirle: tendrá que probarlo para medir la compensación (no hay índice libre).

Y si usa con frecuencia las mismas cláusulas WHERE comunes contra Enroll_Date para rangos específicos y bien definidos (por ejemplo, el trimestre actual o el año hasta la fecha), podría agregar índices filtrados coincidentes que reduzcan aún más esa E / S (pero siempre hay un compensación).

También puede considerar poner un índice agrupado en la tabla base. Este no parece ser uno de esos casos de uso muy raros que se benefician de un montón.


Acabo de confirmar con nuestro departamento de informática y parece que no puedo crear este tipo de vista. Pero aún así apreciará su consejo, y ayudará a otros que puedan usarlo.
Thinkinger

1
¿Piensa su TI que hay una diferencia significativa entre una vista indexada e índices adicionales o diferentes en la tabla base? No ser combativo, solo curioso, porque muchas personas tienen ideas erróneas sobre las vistas indexadas. Me gusta pensar en ellos como un índice agrupado adicional y más delgado en la tabla, pero con menos filas.
Aaron Bertrand

@Thinkinger también, las vistas indexadas no son solo EE. La coincidencia de vista indexada es solo EE. Puedes apuntarlos directamente con NOEXPAND.
usr
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.