La consulta se ejecuta muy lentamente, ¿hay alguna forma de mejorarla más?

9

Tengo la siguiente consulta, y debido a muchas SUMllamadas a funciones, mi consulta se está ejecutando demasiado lento. Tengo muchos registros en mi base de datos y me gustaría obtener un informe del año actual y del año pasado (últimos 30 días, últimos 90 días y últimos 365 días) para cada uno:

SELECT 
    b.id as [ID]
    ,d.[Title] as [Title]
    ,e.Class as [Class]

    ,Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-1,GETDATE()) THEN a.col1 ELSE 0 END) as [Current - Last 30 Days Col1]
    ,Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-1,GETDATE()) THEN a.col2 ELSE 0 END) as [Current - Last 30 Days Col2]

    ,Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-1,GETDATE()) THEN a.col1 ELSE 0 END) as [Current - Last 90 Days Col1]
    ,Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-1,GETDATE()) THEN a.col2 ELSE 0 END) as [Current - Last 90 Days Col2]

    ,Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-1,GETDATE()) THEN a.col1 ELSE 0 END) as [Current - Last 365 Days Col1]
    ,Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-1,GETDATE()) THEN a.col2 ELSE 0 END) as [Current - Last 365 Days Col2]

    ,Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-13,GETDATE()) and a.DateCol <= DATEADD(MONTH,-12,GETDATE()) THEN a.col1 ELSE 0 END) as [Last year - Last 30 Days Col1]
    ,Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-13,GETDATE()) and a.DateCol <= DATEADD(MONTH,-12,GETDATE()) THEN a.col2 ELSE 0 END) as [Last year - Last 30 Days Col2]

    ,Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-5,GETDATE()) and a.DateCol <= DATEADD(QUARTER,-4,GETDATE()) THEN a.col1 ELSE 0 END) as [Last year - Last 90 Days Col1]
    ,Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-5,GETDATE()) and a.DateCol <= DATEADD(QUARTER,-4,GETDATE()) THEN a.col2 ELSE 0 END) as [Last year - Last 90 Days Col2]

    ,Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-2,GETDATE()) and a.DateCol <= DATEADD(YEAR,-1,GETDATE()) THEN a.col1 ELSE 0 END) as [Last year - Last 365 Days Col1]
    ,Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-2,GETDATE()) and a.DateCol <= DATEADD(YEAR,-1,GETDATE()) THEN a.col2 ELSE 0 END) as [Last year - Last 365 Days Col2]


    FROM 
    tb1 a
INNER JOIN 
    tb2 b on a.id=b.fid and a.col3 = b.col4
INNER JOIN 
    tb3 c on b.fid = c.col5
INNER JOIN       
    tb4 d on c.id = d.col6
INNER JOIN 
    tb5 e on c.col7 = e.id
GROUP BY
    b.id, d.Title, e.Class

¿Alguien tiene alguna idea de cómo puedo mejorar mi consulta para que se ejecute más rápido?

EDITAR: Me animaron a mover la DATEADDllamada de función a la whereinstrucción y cargar los dos primeros años primero y luego filtrarlos en columnas, pero no estoy seguro de que la respuesta sugerida se ejecute y funcione, se puede encontrar aquí: https: // stackoverflow. com / a / 59944426/12536284

Si está de acuerdo con la solución anterior, muéstreme cómo puedo aplicarla en mi consulta actual.

Solo para su información, estoy usando este SP en C #, Entity Framework (DB-First), algo como esto:

var result = MyDBEntities.CalculatorSP();
Jim
fuente
44
Muéstranos tu plan de ejecución ...
Dale K
1
EN cualquier cosa - es lo que puede hacer que la consulta sea lenta
Fabio
2
Nuevamente, publique el plan de ejecución.
SQL Police
2
Aún no estamos viendo el Execution Plan. Por favor
publíquelo

Respuestas:

10

Como ya se mencionó, el plan de ejecución será realmente útil en este caso. Según lo que ha mostrado, parece que ha extraído 12 columnas de 15 columnas en total tb1 (a), por lo que puede intentar ejecutar su consulta sin unir y solo tb1para ver si su consulta funciona como se esperaba. Como no puedo ver nada malo con sus llamadas a la función SUMA, mi mejor suposición es que tiene un problema con sus uniones, sugeriría hacer lo siguiente. Puede comenzar excluyendo la última unión, por ejemplo, INNER JOIN tb5 e on c.col7 = e.idy cualquier uso relacionado de la misma, como e.Class as [Class]ye.Classen su grupo por declaración. No lo vamos a excluir por completo, esto es solo una prueba para asegurarnos de si el problema está en eso o no, si su consulta se ejecuta mejor y, como se esperaba, puede intentar usar una tabla temporal como solución alternativa en lugar de la última unión , algo como esto:

SELECT *
INTO #Temp
FROM
  (
     select * from tb5
  ) As tempTable;

SELECT 
    b.id as [ID]
    ,d.[Title] as [Title]
    ,e.Class as [Class]

    -- SUM Functions

FROM 
    tb1 a
INNER JOIN 
    tb2 b on a.id=b.fid and a.col3 = b.col4
INNER JOIN 
    tb3 c on b.fid = c.col5
INNER JOIN       
    tb4 d on c.id = d.col6
INNER JOIN 
    #Temp e on c.col7 = e.id
GROUP BY
    b.id, d.Title, e.Class

En realidad, las tablas temporales son tablas que existen temporalmente en el servidor SQL. Las tablas temporales son útiles para almacenar los conjuntos de resultados inmediatos a los que se accede varias veces. Puede leer más sobre esto aquí https://www.sqlservertutorial.net/sql-server-basics/sql-server-temporary-tables/ Y aquí https://codingsight.com/introduction-to-temporary-tables-in -servidor SQL/

También recomendaría encarecidamente que, si está utilizando el Procedimiento almacenado, establezca el valor NOCOUNTen ON, también puede proporcionar un aumento significativo del rendimiento, ya que el tráfico de red se reduce considerablemente:

SET NOCOUNT ON
SELECT *
INTO #Temp
-- The rest of code

Basado en esto :

SET NOCOUNT ON es una instrucción set que evita el mensaje que muestra el número de filas afectadas por las instrucciones de consulta T-SQL. Esto se usa dentro de procedimientos almacenados y disparadores para evitar mostrar el mensaje de las filas afectadas. El uso de SET NOCOUNT ON dentro de un procedimiento almacenado puede mejorar el rendimiento del procedimiento almacenado por un margen significativo.

Salah Akbari
fuente
1
¿Puede explicar por qué copiar todo tb5a la #Temptabla y unirse a la tabla temporal funciona más rápido que unirse tb5directamente? simplemente contienen los mismos datos (y es #Tempposible que les falte un índice si existiera tb5). Realmente no puedo entender por qué esto es más eficiente (por lo que sé, debería ser menos eficiente copiar todos los datos y unirse).
zig
2
@zig Tienes razón en este caso, pero ¿qué tb5pasa si se encuentra en otro servidor? En este caso, usar una tabla temporal es definitivamente más rápido que la unión directa a otro servidor. Eso fue solo una sugerencia para probar y ver si algo ha cambiado. Tuve una situación similar en el pasado, y parece que afortunadamente la tabla temporal también ha ayudado al OP en este caso.
Salah Akbari
2

El mejor enfoque es insertar en una tabla variable / tabla hash (si el recuento de filas es pequeño, use una variable de tabla o use una tabla hash si el recuento de filas es bastante grande). Luego actualice la agregación y finalmente seleccione de la variable de tabla o tabla hash. Examinar el plan de consulta es necesario.

DECLARE @MYTABLE TABLE (ID INT, [Title] VARCHAR(500), [Class] VARCHAR(500),
[Current - Last 30 Days Col1] INT, [Current - Last 30 Days Col2] INT,
[Current - Last 90 Days Col1] INT,[Current - Last 90 Days Col2] INT,
[Current - Last 365 Days Col1] INT, [Current - Last 365 Days Col2] INT,
[Last year - Last 30 Days Col1] INT, [Last year - Last 30 Days Col2] INT,
[Last year - Last 90 Days Col1] INT, [Last year - Last 90 Days Col2] INT,
[Last year - Last 365 Days Col1] INT, [Last year - Last 365 Days Col2] INT)



INSERT INTO @MYTABLE(ID, [Title],[Class], 
[Current - Last 30 Days Col1], [Current - Last 30 Days Col2],
[Current - Last 90 Days Col1], [Current - Last 90 Days Col2],
[Current - Last 365 Days Col1], [Current - Last 365 Days Col2],
[Last year - Last 30 Days Col1], [Last year - Last 30 Days Col2],
[Last year - Last 90 Days Col1], [Last year - Last 90 Days Col2],
[Last year - Last 365 Days Col1], [Last year - Last 365 Days Col2]
  )
SELECT    b.id  ,d.[Title] ,e.Class ,0,0,0,0,0,0,0,0,0,0,0,0        
FROM     tb1 a
INNER JOIN   tb2 b on a.id=b.fid and a.col3 = b.col4
INNER JOIN   tb3 c on b.fid = c.col5
INNER JOIN   tb4 d on c.id = d.col6
INNER JOIN  tb5 e on c.col7 = e.id
GROUP BY b.id, d.Title, e.Class

UPDATE T 
SET [Current - Last 30 Days Col1]=K.[Current - Last 30 Days Col1] , 
[Current - Last 30 Days Col2]    =K.[Current - Last 30 Days Col2],
[Current - Last 90 Days Col1]    = K.[Current - Last 90 Days Col1], 
[Current - Last 90 Days Col2]    =K.[Current - Last 90 Days Col2] ,
[Current - Last 365 Days Col1]   =K.[Current - Last 365 Days Col1], 
[Current - Last 365 Days Col2]   =K.[Current - Last 365 Days Col2],
[Last year - Last 30 Days Col1]  =K.[Last year - Last 30 Days Col1],
 [Last year - Last 30 Days Col2] =K.[Last year - Last 30 Days Col2],
[Last year - Last 90 Days Col1]  =K.[Last year - Last 90 Days Col1], 
[Last year - Last 90 Days Col2]  =K.[Last year - Last 90 Days Col2],
[Last year - Last 365 Days Col1] =K.[Last year - Last 365 Days Col1],
 [Last year - Last 365 Days Col2]=K.[Last year - Last 365 Days Col2]
    FROM @MYTABLE T JOIN 
     (
SELECT 
    b.id as [ID]
    ,ISNULL(Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-1,GETDATE()) THEN a.col1 ELSE 0 END),0) as [Current - Last 30 Days Col1]
    ,ISNULL(Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-1,GETDATE()) THEN a.col2 ELSE 0 END),0) as [Current - Last 30 Days Col2]

    ,ISNULL(Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-1,GETDATE()) THEN a.col1 ELSE 0 END),0) as [Current - Last 90 Days Col1]
    ,ISNULL(Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-1,GETDATE()) THEN a.col2 ELSE 0 END),0) as [Current - Last 90 Days Col2]

    ,ISNULL(Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-1,GETDATE()) THEN a.col1 ELSE 0 END),0) as [Current - Last 365 Days Col1]
    ,ISNULL(Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-1,GETDATE()) THEN a.col2 ELSE 0 END),0) as [Current - Last 365 Days Col2]

    ,ISNULL(Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-13,GETDATE()) and a.DateCol <= DATEADD(MONTH,-12,GETDATE()) THEN a.col1 ELSE 0 END),0) as [Last year - Last 30 Days Col1]
    ,ISNULL(Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-13,GETDATE()) and a.DateCol <= DATEADD(MONTH,-12,GETDATE()) THEN a.col2 ELSE 0 END),0) as [Last year - Last 30 Days Col2]

    ,ISNULL(Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-5,GETDATE()) and a.DateCol <= DATEADD(QUARTER,-4,GETDATE()) THEN a.col1 ELSE 0 END),0) as [Last year - Last 90 Days Col1]
    ,ISNULL(Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-5,GETDATE()) and a.DateCol <= DATEADD(QUARTER,-4,GETDATE()) THEN a.col2 ELSE 0 END),0) as [Last year - Last 90 Days Col2]

    ,ISNULL(Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-2,GETDATE()) and a.DateCol <= DATEADD(YEAR,-1,GETDATE()) THEN a.col1 ELSE 0 END),0) as [Last year - Last 365 Days Col1]
    ,ISNULL(Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-2,GETDATE()) and a.DateCol <= DATEADD(YEAR,-1,GETDATE()) THEN a.col2 ELSE 0 END),0) as [Last year - Last 365 Days Col2]
    FROM     tb1 a
INNER JOIN   tb2 b on a.id=b.fid and a.col3 = b.col4
INNER JOIN   tb3 c on b.fid = c.col5
INNER JOIN   tb4 d on c.id = d.col6
INNER JOIN  tb5 e on c.col7 = e.id
GROUP BY    b.id
) AS K ON T.ID=K.ID


SELECT *
FROM @MYTABLE
Harshana
fuente
0

Supongo que tb1 es una tabla grande (en relación con tb2, tb3, tb4 y tb5).

Si es así, tiene sentido restringir la selección de esa tabla (con una cláusula WHERE).

Si solo se usa una pequeña parte de tb1, por ejemplo, porque las uniones con tb2, tb3, tb4 y tb5 reducen las filas necesarias a solo un pequeño porcentaje, entonces debe verificar si las tablas están indexadas en las columnas que usa en las uniones .

Si se usa una gran parte de tb1, entonces puede tener sentido agrupar sus resultados antes de unirlo a tb2, tb3, tb4 y tb5. A continuación se muestra un ejemplo de eso.

SELECT 
    b.id as [ID]
    ,d.[Title] as [Title]
    ,e.Class as [Class]
    ,SUM(a.[Current - Last 30 Days Col1]) AS [Current - Last 30 Days Col1]
    ,SUM(a.[Current - Last 30 Days Col2]) AS [Current - Last 30 Days Col2]
    ,SUM(a.[Current - Last 90 Days Col1]) AS [Current - Last 90 Days Col1]
    -- etc.
    FROM (
      SELECT a.id, a.col3

      ,Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-1,GETDATE()) THEN a.col1 ELSE 0 END) as [Current - Last 30 Days Col1]
      ,Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-1,GETDATE()) THEN a.col2 ELSE 0 END) as [Current - Last 30 Days Col2]

      ,Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-1,GETDATE()) THEN a.col1 ELSE 0 END) as [Current - Last 90 Days Col1]
      ,Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-1,GETDATE()) THEN a.col2 ELSE 0 END) as [Current - Last 90 Days Col2]

      ,Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-1,GETDATE()) THEN a.col1 ELSE 0 END) as [Current - Last 365 Days Col1]
      ,Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-1,GETDATE()) THEN a.col2 ELSE 0 END) as [Current - Last 365 Days Col2]

      ,Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-13,GETDATE()) and a.DateCol <= DATEADD(MONTH,-12,GETDATE()) THEN a.col1 ELSE 0 END) as [Last year - Last 30 Days Col1]
      ,Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-13,GETDATE()) and a.DateCol <= DATEADD(MONTH,-12,GETDATE()) THEN a.col2 ELSE 0 END) as [Last year - Last 30 Days Col2]

      ,Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-5,GETDATE()) and a.DateCol <= DATEADD(QUARTER,-4,GETDATE()) THEN a.col1 ELSE 0 END) as [Last year - Last 90 Days Col1]
      ,Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-5,GETDATE()) and a.DateCol <= DATEADD(QUARTER,-4,GETDATE()) THEN a.col2 ELSE 0 END) as [Last year - Last 90 Days Col2]

      ,Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-2,GETDATE()) and a.DateCol <= DATEADD(YEAR,-1,GETDATE()) THEN a.col1 ELSE 0 END) as [Last year - Last 365 Days Col1]
      ,Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-2,GETDATE()) and a.DateCol <= DATEADD(YEAR,-1,GETDATE()) THEN a.col2 ELSE 0 END) as [Last year - Last 365 Days Col2]

      FROM  tb1 a
      WHERE a.DateCol >= DATEADD(YEAR,-2,GETDATE())
      GROUP BY a.id, a.col3
    ) AS a
INNER JOIN 
    tb2 b on a.id=b.fid and a.col3 = b.col4
INNER JOIN 
    tb3 c on b.fid = c.col5
INNER JOIN       
    tb4 d on c.id = d.col6
INNER JOIN 
    tb5 e on c.col7 = e.id
GROUP BY
    b.id, d.Title, e.Class
Gert-Jan
fuente
Sería mucho mejor ver primero el plan de ejecución y luego tomar decisiones sobre la creación de índices y la recreación de estadísticas.
Policía de SQL el
Realmente odio que mi publicación obtenga una puntuación negativa sin ninguna explicación de por qué. Por supuesto, estoy de acuerdo en que para llegar al corazón del problema de rendimiento, uno debe inspeccionar el plan de ejecución. Dicho esto, mantengo mi recomendación sobre verificar los índices de cualquier clave externa que sea relevante en la consulta.
Gert-
1
"Asumes" algo sin saberlo. Entonces publicas una respuesta sobre la base de lo desconocido. Por lo tanto, voto negativo. Es mejor indicarle al OP que mejore su pregunta publicando el plan de ejecución.
Policía de SQL el
Eso no es todo lo que escribí. Personalmente, solo votaría negativamente si la respuesta es mala o incorrecta, no cuando simplemente no estoy de acuerdo. Pero gracias por responder.
Gert-
En cierto modo está mal, porque ¿cómo puedes probar que es correcto?
Policía de SQL el
0

Para optimizar dichos cálculos, puede considerar calcular previamente algunos de los valores. La idea de los cálculos previos es reducir el número de filas que deben leerse o continuar.

Una forma de lograr esto es usar una vista indizada y dejar que el motor haga los cálculos por sí mismo. Como este tipo de vistas tiene algunas limitaciones, puede terminar creando una tabla simple y realizando los cálculos. Básicamente, depende de las necesidades del negocio.

Entonces, en el ejemplo a continuación, estoy creando una tabla con RowIDy RowDatetimecolumnas e insertando 1 millón de filas. Estoy usando una vista indexada para contar las entidades por días, por lo que en lugar de consultar 1 millón de filas por año, consultaré 365 filas por año para contar estas métricas.

DROP TABLE IF EXISTS [dbo].[DataSource];
GO

CREATE TABLE [dbo].[DataSource]
(
    [RowID] BIGINT IDENTITY(1,1) PRIMARY KEY
   ,[RowDateTime] DATETIME2
);

GO

DROP VIEW IF EXISTS [dbo].[vw_DataSource];
GO

CREATE VIEW [dbo].[vw_DataSource] WITH SCHEMABINDING
AS
SELECT YEAR([RowDateTime]) AS [Year]
      ,MONTH([RowDateTime]) AS [Month]
      ,DAY([RowDateTime]) AS [Day]
      ,COUNT_BIG(*) AS [Count]
FROM [dbo].[DataSource]
GROUP BY YEAR([RowDateTime])
        ,MONTH([RowDateTime])
        ,DAY([RowDateTime]);
GO

CREATE UNIQUE CLUSTERED INDEX [IX_vw_DataSource] ON [dbo].[vw_DataSource]
(
    [Year] ASC,
    [Month] ASC,
    [Day] ASC
);

GO

DECLARE @min bigint, @max bigint
SELECT @Min=1 ,@Max=1000000

INSERT INTO [dbo].[DataSource] ([RowDateTime])
SELECT TOP (@Max-@Min+1) DATEFROMPARTS(2019,  1.0 + floor(12 * RAND(convert(varbinary, newid()))), 1.0 + floor(28 * RAND(convert(varbinary, newid())))          )       
FROM master..spt_values t1 
CROSS JOIN master..spt_values t2

GO


SELECT *
FROM [dbo].[vw_DataSource]


SELECT SUM(CASE WHEN DATEFROMPARTS([Year], [Month], [Day]) >= DATEADD(MONTH,-1,GETDATE()) THEN [Count] ELSE 0 END) as [Current - Last 30 Days Col1]
      ,SUM(CASE WHEN DATEFROMPARTS([Year], [Month], [Day]) >= DATEADD(QUARTER,-1,GETDATE()) THEN [Count] ELSE 0 END) as [Current - Last 90 Days Col1]
      ,SUM(CASE WHEN DATEFROMPARTS([Year], [Month], [Day]) >= DATEADD(YEAR,-1,GETDATE()) THEN [Count] ELSE 0 END) as [Current - Last 365 Days Col1]
FROM [dbo].[vw_DataSource];

El éxito de dicha solución depende en gran medida de cómo se distribuyen los datos y cuántas filas tiene. Por ejemplo, si tiene una entrada por día para cada día del año, la vista y la tabla tendrán la misma coincidencia de filas, por lo que las operaciones de E / S no se reducirán.

Además, lo anterior es solo un ejemplo de materializar los datos y leerlos. En su caso, es posible que deba agregar más columnas a la definición de la vista.

gotqn
fuente
0

Usaría una tabla de búsqueda "Fechas" para unir mis datos con un índice en DatesId. Utilizo las fechas como filtro cuando quiero buscar datos históricos. La unión es rápida y, por lo tanto, el filtrado como DatesId es el índice primario agrupado (clave primaria). Agregue la columna de fecha (como columna incluida) para su tabla de datos también.

La tabla de fechas tiene las siguientes columnas:

DatesId, Date, Year, Quarter, YearQuarter, MonthNum, MonthNameShort, YearWeek, WeekNum, DayOfYear, DayOfMonth, DayNumOfWeek, DayName

Datos de ejemplo: 20310409 2031-04-09 2031 2 2031-Q2 4 abril abr 2031_15 15 99 9 3 miércoles

Puede enviarme un PM si desea un csv de esto para poder importarlo a la base de datos, pero estoy seguro de que puede encontrar fácilmente algo como esto en línea y hacer el suyo.

También agrego una columna de identidad para que pueda obtener un número entero para cada fecha. Esto hace que sea un poco más fácil trabajar con él, pero no es un requisito.

SELECT * FROM dbo.dates where dateIndex BETWEEN (getDateIndexDate(getDate())-30 AND getDateIndexDate(getDate())+0) --30 days ago

Esto me permite volver fácilmente a un cierto período. Es bastante fácil crear sus propios puntos de vista sobre esto. Por supuesto, también puede usar la función ROW_NUMBER () para hacer esto durante años, semanas, etc.

Una vez que tengo el rango de fechas que quiero, me uno a los datos. ¡Funciona muy rápido!

Starbyone
fuente
0

Como siempre está agrupando valores basados ​​en un número entero de meses, primero los agruparía por mes en una subconsulta en la cláusula from. Esto es similar a usar una tabla temporal. No estoy seguro de si esto realmente aceleraría su consulta.

SELECT f.id, f.[Title], f.Class,
    SUM(CASE WHEN f.MonthDiff = 1 THEN col1 ELSE 0 END) as [Current - Last 30 Days Col1],
    -- etc
FROM (
    SELECT 
        b.id,
        d.[Title],
        e.Class,
        DateDiff(Month, a.DateCol, GETDATE()) as MonthDiff,
        Sum(a.col1) as col1,
        Sum(a.col2) as col2
    FROM  tb1 a
    INNER JOIN tb2 b on a.id = b.fid and a.col3 = b.col4
    INNER JOIN tb3 c on b.fid = c.col5
    INNER JOIN tb4 d on c.id = d.col6
    INNER JOIN tb5 e on c.col7 = e.id
    WHERE a.DateCol between DATEADD(YEAR,-2,GETDATE() and GETDATE()
    GROUP BY b.id, d.Title, e.Class, DateDiff(Month,  a.DateCol, GETDATE())
) f
group by f.id, f.[Title], f.Class
Jeremy Lakeman
fuente
-2

Para mejorar la velocidad de la consulta SQL, debe agregar índices. Para cada tabla unida, debe agregar un índice.

Como este ejemplo de código para Oracle:

CREATE INDEX supplier_idx
ON supplier (supplier_name);
usuario12467638
fuente
Esta no es una mala sugerencia. puede ver en el OP que se crea una tabla temporal sin índice: INNER JOIN #Temp e en c.col7 = e.id. Si bien hay margen de mejora en la respuesta, no creo que deba ser rechazada en masa. particularmente para un nuevo usuario.
smoore4
@ smoore4 De acuerdo, esta opción de downvoting sin un argumento claro debe eliminarse. Hay un gran mal uso de esta funcionalidad
Greggz