2010-03-10 7 views
6

Supongamos que tengo una tabla muy larga (~ 35 millones de filas) llamada TimeCard con solo 5 columnas (tableID, CompanyID, ID de usuario, ProjectID, DailyHoursWorked, entryDate). Esta es una tabla bastante sencilla que registra las horas trabajadas de los empleados por día por proyecto por empresa.Lentitud en la vista indizada para SQL 2005

Ahora necesito generar un informe para conocer las horas trabajadas totales de los empleados por mes por proyecto para una empresa determinada. En lugar de realizar la agregación necesaria cuando se ejecuta el informe, quiero construir una estructura de datos similar a una tabla que ya tenga agregados todos los datos de la empresa/proyecto/usuario por mes, de modo que cuando se ejecute el informe, solo pueda consultar esa estructura de datos directamente sin realizar ninguna agregación en tiempo de ejecución, ya que ~ 35 millones de registros pueden tardar unos minutos.

Así que tengo 2 formas diferentes. Uno crea una tabla física adicional con (CompanyID, UserID, ProjectID, MonthlyHoursWorked, Month) como mis columnas y simplemente usa el disparador en la tabla TimeCard para modificar los valores en la tabla adicional. O puedo crear una vista indizada. Entonces intenté ambos. La primera vez que probé la vista indizada con el siguiente código:

CREATE VIEW [dbo].[vw_myView] WITH SCHEMABINDING AS 
SELECT 
JobID, 
ProjectID, 
Sum(DailyHoursWorked) AS MonthTotal, 
DATEADD(Month, DATEDIFF(Month, 0, entryDate), 0) AS entryMonth, 
CompanyID, 
COUNT_BIG(*) AS Counter 
FROM 
dbo.TimeCard 
Group By DATEADD(Month, DATEDIFF(Month, 0, entryDate), 0), JobID, ProjectID, CompanyID 

Go 
CREATE UNIQUE CLUSTERED INDEX [IX_someIndex] ON [dbo].[vw_myView] 
(
[CompanyID] ASC, 
[entryMonth] ASC, 
[UserID] ASC, 
[ProjectID] ASC 
) 

La vista indizada creado correcta y total con un total de ~ 5 millones de filas.

Sin embargo, cada vez que borro el caché de SQL, y ejecuto la siguiente consulta: * select * from vw_myView where companyID = 1 *, toma casi 3 minutos. Si voy con la ruta de tabla adicional como mencioné anteriormente, con mi memoria caché borrada, toma alrededor de 4 segundos.

Mis preguntas son, ¿Está indexada? ¿Ver una mala elección para este escenario en particular? En particular, me interesa saber si toda la vista indizada se vuelve a calcular/volver a agregar cada vez que se cambia la tabla subyacente (TimeCard) o cuando se ejecuta una consulta en su contra.

Gracias!

+0

¿Qué edición de SQL Server 2005 estás usando? – RedFilter

+0

En lugar de tener la fecha completa del primer día del mes en tu 'entryMonth', ¿no podrías tener' MONTH (entryDate) 'y posiblemente' YEAR (entryDate) 'como INTs? Me parece mucho más fácil (pero, de nuevo, no sé cuáles son sus requisitos) ... –

Respuesta

0

No utilizaría una vista para esto. Creo que la tabla poblada por el disparador es el camino a seguir. Pero no olvide ajustar los totales de las actualizaciones y eliminaciones, así como los insertos.

+1

Los disparadores no son muy rápidos. –

+0

Las vistas son a menudo más lentas que los disparadores (especialmente si se amontonan una encima de la otra) y se puede escribir un desencadenador para que sea rápido. – HLGEM

2

Si no está usando ya sea el Empresa o edición desarrollador, entonces es necesario utilizar el with (noexpand) pista:

select * 
from vw_myView with (noexpand) 
where companyID = 1 

Cuando cambian los datos subyacentes, la vista sólo se actualizará filas relacionadas con los datos modificados, no toda la tabla. Esto puede tener un impacto adverso en una base de datos OLTP con un alto grado de inserciones, pero si el uso es solo moderado, no debería representar un problema de rendimiento.

Un tip from Microsoft:

Como recomendación general, cualquier modificaciones o cambios a la vista o las tablas base subyacentes a la vista deben llevarse a cabo en lotes si posible, en lugar de simples operaciones. Esto puede reducir algunos gastos generales en el mantenimiento de la vista.

+0

Lo que no entiendo es por qué tardan 3 minutos para una consulta simple si borro la caché y no ha cambiado el valor de la tabla base. ¿De hecho está haciendo toda la agregación nuevamente? – TheYouth

+0

@TheYouth: ¿probó la sugerencia 'with (noexpand)'? – RedFilter

+1

+1 si olvida agregar WITH (NOEXPAND) en las versiones EXPRESS o DEVELOPER de SQL Server, el optimizador no usará la vista indexada, sino que seleccionará de las tablas subyacentes. –

0

No creo que necesite una vista indizada (no digo, la vista indizada es mala/buena idea). Creo que necesita el índice en la columna "CompanyID" y "EntryDate".Después de eso, debe usar la condición where "WHERE CompanyID = @CompanyID AND EntryDate> = @StartDate AND EntryDate < = @EndDate".

Si la tabla se procesa primaramente en el "EntryDate", puede utilizar un índice de clúster en la columna "EntryDate".

Después de esto, creo que la instrucción seleccionada será mucho más rápida que ahora.

+1

el índice agrupado de la vista ya usa CompanyID como su primera columna, eso debería ser el truco, realmente. No veo ningún beneficio en un índice en (CompanyID, EntryDate); parece que no hay consultas con EntryDate, entonces, ¿de qué sirve indexarlo? –

+0

La TimeCard está indexada con entryDate, companyID, userID, projectID, todo en índices separados no agrupados. Lo que no entiendo es por qué tardan 3 minutos para una consulta simple si borro el caché. ¿De hecho está haciendo toda la agregación nuevamente? – TheYouth

+0

Debes filtrarlo por EntryDate. Si ya sabes, ya tienes información agregada sobre enero de 2010, no necesitas volver a seleccionar ti de la tabla principal. Si utiliza el índice de clúster en la columna "Fecha de entrada" (sin el ID de empresa), debe obtener un mejor rendimiento para las selecciones basadas en esta columna. – TcKs

1

Creo que está en el camino correcto con el uso de una vista de índice. Sin embargo, ¿ha colocado índices en la tabla desde la que está consultando? TimeCard para sus columnas agregadas. Debe crear un índice de JobID, ProjectID, entryDate, CompanyID (1 índice). Si usa 1 índice para cada columna, NO resolverá sus problemas porque la Consulta tendrá que usar los 4 índices juntos.

Creo que usar el gatillo será lento pero de una manera diferente. Hará su consulta más rápida pero ralentizará cada inserción que haga en TimeCard. Si decides ir con el disparador, entonces me aseguraré de indexar esa tabla también o también puede ser lento, no 3 minutos lento, pero aún lento para ordenar y devolver datos.

+2

No veo cómo más índices ayudarán, la consulta se selecciona por 'CompanyID', que ya está indexada. – RedFilter

+0

La TimeCard está indexada con entryDate, companyID, userID, projectID, todo en índices separados no agrupados. Lo que no entiendo es por qué tardan 3 minutos para una consulta simple si borro el caché. ¿De hecho está haciendo toda la agregación nuevamente? – TheYouth

+0

Creo que es. Sin embargo, creo que sería mejor si tuviera 1 índice no de clúster para entryDate, companyID, userID, projectID. Tener 4 índices, uno para cada una de esas columnas, realmente no lo ayudará porque su Group By está usando los 4 juntos. Entonces quiere 1 índice que tenga las 4 columnas. –

0

Consideró la posibilidad de particionar la tabla. Puedes pensar en una combinación de lista y tabla de particiones hash.

0

Bueno, la idea de una vista indizada es definitivamente buena, y si puede crear un índice agrupado en ella, perfecto. Debería ser rápido, ¡mucho mejor que 3 minutos para una consulta!

Por otro lado, si esos fragmentos de información solo se actualizan, p. una vez al mes o una vez a la semana (o incluso todas las noches), puede ser mejor colocarlas en una tabla separada DailyTimeCard que se llena/actualiza, p. un paquete de SSIS regularmente.

yo no recomendaría el uso de disparadores para actualizar constantemente una mesa de tal hecho bien - si realmente necesita tener más datos actualizados en cada segundo dado en el día, y luego quedarse con la vista indizada.

Pero, su vista indizada hace un poco de trabajo pesado - se resume, agrupa por y así sucesivamente. Mantenerlo actualizado en todo momento, mientras que su tabla TimeCard subyacente cambia y se actualiza, causará cierta carga en su sistema, es difícil decir cuánto, pero podría ser bastante notable.

Si encuentra una manera de extraer la información que necesita (agrupar y sumar una vez y luego almacenar esa información agregada en una tabla de hechos separada), debe tener tanto: consultas rápidas y rápidas en la tabla DailyTimeCard, como el resto de su sistema debería estar menos cargado con mantener la vista indexada actualizada todo el tiempo.

Quizás no sea la solución que está buscando, pero solo piénselo un momento. ¡Podría funcionar, o no, para usted!

Cuestiones relacionadas