A consulta está sendo executada muito lentamente. Existe alguma maneira de melhorá-la ainda mais?

9

Tenho a seguinte consulta e, devido a muitas SUMchamadas de função, minha consulta está muito lenta. Tenho muitos registros no meu banco de dados e gostaria de obter um relatório do ano atual e do ano passado (Últimos 30 dias, Últimos 90 dias e últimos 365 dias) para cada um:

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

Alguém tem alguma idéia de como posso melhorar minha consulta para executar mais rapidamente?

EDIT: fui encorajado a mover a DATEADDchamada de função para a whereinstrução e carregar os dois primeiros anos primeiro e depois filtrá-los em colunas, mas não tenho certeza de que a resposta sugerida seja executada e funcione, ela pode ser encontrada aqui: https: // stackoverflow. com / a / 59944426/12536284

Se você concorda com a solução acima, mostre-me como posso aplicá-la na minha consulta atual?

Apenas para sua informação, estou usando esse SP em C #, Entity Framework (DB-First), algo como isto:

var result = MyDBEntities.CalculatorSP();
Jim
fonte
4
Mostre-nos o seu plano de execução ...
Dale K
11
Em qualquer coisa - é o que pode tornar a consulta lenta
Fabio
2
Novamente, publique o plano de execução.
SQL Police
2
Ainda não estamos vendo o Execution Plan. Por favor, publique-o
Arun Palanisamy

Respostas:

10

Como já foi mencionado, o plano de execução será realmente útil neste caso. Com base no que você mostrou, parece que extraiu 12 colunas do total de 15 colunas tb1 (a), para que você possa tentar executar sua consulta sem nenhuma junção e apenas contra o tb1para ver se sua consulta está funcionando conforme o esperado. Como não vejo nada de errado com suas chamadas de função SUM, meu melhor palpite é que você tem um problema com suas junções, sugiro que você faça o seguinte. Você pode começar por excluindo o último juntar-se, por exemplo, INNER JOIN tb5 e on c.col7 = e.ide qualquer uso relacionado nisso como e.Class as [Class]ee.Classno seu grupo por declaração. Não vamos excluí-lo completamente, este é apenas um teste para garantir se o problema está relacionado com isso ou não, se sua consulta for melhor e conforme o esperado, você pode tentar usar uma tabela temporária como solução alternativa em vez da última associação , algo assim:

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

Na verdade, tabelas temporárias são tabelas que existem temporariamente no SQL Server. As tabelas temporárias são úteis para armazenar os conjuntos de resultados imediatos que são acessados ​​várias vezes. Você pode ler mais sobre isso aqui https://www.sqlservertutorial.net/sql-server-basics/sql-server-temporary-tables/ E aqui https://codingsight.com/introduction-to-temporary-tables-in -servidor SQL/

Também recomendo vivamente que, se você estiver usando o Stored Procedure, defina o NOCOUNT como ON, ele também pode fornecer um aumento significativo no desempenho, porque o tráfego de rede é bastante reduzido:

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

Baseado em nisso :

SET NOCOUNT ON é uma instrução set que impede a mensagem que mostra o número de linhas afetadas pelas instruções de consulta T-SQL. Isso é usado nos procedimentos e gatilhos armazenados para evitar a exibição da mensagem das linhas afetadas. O uso de SET NOCOUNT ON em um procedimento armazenado pode melhorar o desempenho do procedimento armazenado por uma margem significativa.

Salah Akbari
fonte
11
Você pode explicar por que copiar cópias inteiras tb5da #Temptabela e ingressar na tabela temporária funciona mais rápido do que entrar tb5diretamente? seguramente eles contêm os mesmos dados (e #Temppode estar faltando um índice se ele existisse tb5). Realmente não consigo entender por que isso é mais eficiente (pelo que sei, deve ser menos eficiente copiar todos os dados e ingressar).
zig
2
@ zig Você está certo neste caso, mas e se ele tb5estiver localizado em outro servidor? Nesse caso, o uso de uma tabela temporária é definitivamente mais rápido que a associação direta a outro servidor. Essa foi apenas uma sugestão para testar e verificar se alguma coisa foi alterada. Tive uma situação semelhante no passado e, felizmente, parece que a tabela temporária também ajudou o OP nesse caso.
Salah Akbari
2

A melhor abordagem é inserir em uma variável de tabela / tabela de hash (se a contagem de linhas for pequena, use uma variável de tabela ou use uma tabela de hash se a contagem de linhas for muito grande). Atualize a agregação e, finalmente, selecione na variável da tabela ou na tabela de hash. É necessário analisar o plano de consulta.

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
fonte
0

Presumo que TB1 seja uma tabela grande (relativa a TB2, TB3, TB4 e TB5).

Nesse caso, faz sentido restringir a seleção dessa tabela (com uma cláusula WHERE).

Se apenas uma pequena parte de tb1 for usada, por exemplo, porque as junções com tb2, tb3, tb4 e tb5 reduzem as linhas necessárias para apenas alguns por cento, verifique se as tabelas estão indexadas nas colunas usadas nas junções .

Se uma grande parte de tb1 for usada, pode fazer sentido agrupar seus resultados antes de associá-los a tb2, tb3, tb4 e tb5. Abaixo está um exemplo disso.

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
fonte
Seria muito melhor visualizar primeiro o plano de execução e depois tomar decisões sobre a criação de índices e a recriação de estatísticas.
SQL Police
Eu realmente odeio que meu post receba uma pontuação negativa sem qualquer explicação do porquê. É claro que concordo que, para chegar ao cerne da questão do desempenho, é preciso inspecionar o plano de execução. Dito isto, mantenho minha recomendação sobre a verificação dos índices em busca de chaves estrangeiras relevantes na consulta.
Gert-Jan
11
Você "assume" algo sem saber. Então você posta uma resposta com base no desconhecido. Portanto, voto negativo. É melhor instruir o OP a melhorar sua pergunta publicando o plano de execução.
SQL Police
Não foi tudo o que escrevi. Pessoalmente, eu votaria apenas se a resposta fosse ruim ou errada, não quando simplesmente discordasse. Mas obrigado por responder.
Gert-Jan
De certa forma, está errado, porque como você pode provar que está correto?
SQL Police
0

Basta usar colunas computadas

Exemplo

ALTER TABLE tb1 ADD [Current - Last 30 Days Col1] AS (CASE WHEN a.DateCol >= DATEADD(MONTH,-1,GETDATE()) THEN a.col1 ELSE 0 END) PERSISTED;

Especificar colunas computadas em uma tabela

Dawid Wekwejt
fonte
0

Para otimizar tais cálculos, considere pré-calcular alguns dos valores. A idéia dos pré-cálculos é reduzir o número de linhas que precisam ser lidas ou prosseguidas.

Uma maneira de conseguir isso é usar uma exibição indexada e deixar o mecanismo para fazer os cálculos sozinho. Como esse tipo de visualização tem algumas limitações, você cria uma tabela simples e executa os cálculos. Basicamente, isso depende das necessidades do negócio.

Assim, no exemplo abaixo estou criando uma tabela com RowIDe RowDatetimecolunas e inserir 1 milhão de linhas. Eu estou usando uma exibição indexada para contar as entidades por dias, então, em vez de consultar 1 milhão de linhas por ano, consultarei 365 linhas por ano para contar essas 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];

O sucesso dessa solução depende muito de como os dados são distribuídos e de quantas linhas você possui. Por exemplo, se você tiver uma entrada por dia para cada dia do ano, a visualização e a tabela terão a mesma correspondência de linhas, portanto, as operações de E / S não serão reduzidas.

Além disso, o acima é apenas um exemplo de materialização e leitura dos dados. No seu caso, pode ser necessário adicionar mais colunas à definição da visualização.

gotqn
fonte
0

Eu usaria uma tabela "Datas" da tabela de pesquisa para associar meus dados a um índice em DatesId. Uso as datas como um filtro quando quero procurar dados históricos. A junção é rápida e, portanto, a filtragem como DatesId é o índice primário em cluster (chave primária). Adicione a coluna de data (como coluna incluída) para sua tabela de dados também.

A tabela de datas possui as seguintes colunas:

DatasId, Data, Ano, Trimestre, AnoQuarter, MonthNum, MonthNameShort, YearWeek, WeekNum, DayOfYear, DayOfMonth, DayNumOfWeek, DayName

Dados de exemplo: 20310409 2031-04-09 2031 2 2031-Q2 4 de abril de 2031_15 15 99 9 3 quarta-feira

Você pode me enviar uma MP se quiser um csv disso para poder importá-lo para o banco de dados, mas tenho certeza de que pode encontrar facilmente algo assim on-line e criar o seu.

Também adiciono uma coluna de identidade para que você possa obter um número inteiro para cada data. Isso facilita um pouco o trabalho, mas não é um requisito.

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

Isso me permite voltar facilmente a um determinado período. É muito fácil criar seus próprios pontos de vista sobre isso. Obviamente, você pode usar a função ROW_NUMBER () para fazer isso por anos, semanas etc. também.

Depois de obter a data pretendida, associo-me aos dados. Funciona muito rápido!

starbyone
fonte
0

Como você está sempre agrupando valores com base em um número inteiro de meses, eu primeiro agruparia por mês em uma subconsulta na cláusula from. Isso é semelhante ao uso de uma tabela temporária. Não tenho certeza se isso realmente aceleraria sua 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
fonte
-2

Para melhorar a velocidade da consulta SQL, você deve adicionar índices. Para cada tabela ingressada, você precisa adicionar um índice.

Como este exemplo de código para o oracle:

CREATE INDEX supplier_idx
ON supplier (supplier_name);
user12467638
fonte
isso não é uma má sugestão. você vê no OP que uma tabela temporária é criada sem índice - INNER JOIN #Temp e em c.col7 = e.id. Embora exista espaço para melhorias na resposta, não acho que deva ser diminuída em massa. particularmente para um novo usuário.
smoore4 10/02
@ smoore4 Concordo, esta opção de voto negativo sem um argumento claro deve ser removida. Há um grande uso indevido dessa funcionalidade
Greggz