2016-12-14 1 views
0

Je recherche des astuces/astuces pour améliorer les performances d'une procédure stockée avec plusieurs instructions SELECT insérées dans une table. Tous les objets auxquels je participe sont déjà indexés.Améliorer les performances d'une requête sql

Je crois que la raison de cette procédure stockée prend presque une heure à courir est parce qu'il ya plusieurs instructions SELECT suivantes en utilisant deux vues: rvw_FinancialLineItemValues ​​et rvw_FinancialLineItems

En outre, chaque instruction SELECT utilise des valeurs codées en dur spécifiques pour AccountNumber, LineItemTypeID et quelques autres valeurs de champ provenant de deux vues mentionnées ci-dessus.

Est-ce que cela améliorerait les performances si je crée une table temporaire, qui récupère TOUTES les données nécessaires pour ces instructions SELECT en même temps, puis utilise cette table temporaire dans ma jointure?

Existe-t-il d'autres moyens d'améliorer les performances et la gestion?

 SELECT 
     @scenarioid, 
     @portfolioid, 
     pa.Id, 
     pa.ExternalID,  
     (select value from fn_split(i.AccountNumber,'.') where id = 1), 
     ac.[Description], 
     cl.Name, 
     NullIf((select value from fn_split(i.AccountNumber,'.') where id = 2),''), 
     NullIf((select value from fn_split(i.AccountNumber,'.') where id = 3),''), 
     ty.Name, 
     v.[Date], 
     cast(SUM(v.Amount) as decimal(13,2)), 
     GETDATE() 

    FROM rvw_FinancialLineItems i 
    INNER JOIN rvw_Scenarios sc 
     ON i.ScenarioId = sc.Id 
     AND sc.Id = @scenarioid 
     AND sc.PortfolioId = @portfolioid 
    INNER JOIN #pa AS pa 
     ON i.PropertyAssetID = pa.Id 
    INNER JOIN rvw_FinancialLineItemValues v 
     ON i.ScenarioId = v.ScenarioId 
     AND i.PropertyAssetID = v.PropertyAssetID 
     AND i.Id = v.FinancialLineItemId 
     AND ((i.BusinessEntityTypeId = 11 
     AND i.LineItemTypeId = 3002) 
     OR (i.LineItemTypeId IN (2005, 2010, 2003, 2125, 2209, 5012, 6001) 
     AND i.ModeledEntityKey = 1)) 
     AND i.AccountNumber not in ('401ZZ','403ZZ') 
    AND i.AccountNumber not in ('401XX') 
     AND i.AccountNumber not in ('40310','41110','42010','41510','40190','40110') -- exclude lease-level revenues selected below 
     AND v.[Date] BETWEEN @fromdate AND 
      CASE 
      WHEN pa.AnalysisEnd < @todate THEN pa.AnalysisEnd 
      ELSE @todate 
      END 
     AND v.ResultSet IN (0, 4) 
    INNER JOIN rvw_Portfolios po 
     ON po.Id = @portfolioid 
    INNER JOIN Accounts ac 
     ON po.ChartOfAccountId = ac.ChartOfAccountId 
     AND i.AccountNumber = ac.AccountNumber 
     AND ac.HasSubAccounts = 0 
    INNER JOIN fn_LookupClassTypes() cl 
     ON ac.ClassTypeId = cl.Id 
    INNER JOIN LineItemTypes ty 
     ON ac.LineItemTypeId = ty.Id 
    LEFT JOIN OtherRevenues r 
     ON i.PropertyAssetID = r.PropertyAssetID 
     AND i.AccountNumber = r.AccountID 
     AND v.[Date] BETWEEN r.[Begin] AND r.[End] 
    WHERE (r.IsMemo IS NULL 
    OR r.IsMemo = 0) 
    GROUP BY pa.AnalysisBegin 
       ,pa.Id 
       ,pa.ExternalID 
       ,i.AccountNumber 
       ,ac.[Description] 
       ,cl.Name 
       ,ty.Name 
       ,v.[Date] 
    HAVING SUM(v.amount) <> 0 
+1

Avez-vous consulté le plan d'exécution de la requête et/ou l'estimation pour voir si un noeud particulier est à l'origine du temps d'exécution? – Anand

Répondre

1

Première chose, quelle UDF fn_split() utilisez-vous? Si vous n'utilisez pas le UDF en ligne table-évalué, alors ceci est notoirement lent.

Deuxièmement, est-ce que le UDF fn_LookupClassTypes() est une table UDF en ligne?Si ce n'est pas le cas, convertissez-le en UDF en ligne avec table. En dernier, votre requête SQL a eu quelques redondances. Essayez ceci et voyez ce qu'il fait.

SELECT @scenarioid, @portfolioid, pa.Id, pa.ExternalID,  
     (select value from fn_split(i.AccountNumber,'.') 
     where id = 1), ac.[Description], cl.Name, 
    NullIf((select value from fn_split(i.AccountNumber,'.') 
      where id = 2),''), 
    NullIf((select value from fn_split(i.AccountNumber,'.') 
      where id = 3),''), ty.Name, v.[Date], 
    cast(SUM(v.Amount) as decimal(13,2)), GETDATE() 

FROM rvw_FinancialLineItems i 
    JOIN rvw_Scenarios sc ON sc.Id = i.ScenarioId 
    JOIN #pa AS pa ON pa.Id = i.PropertyAssetID 
    JOIN rvw_FinancialLineItemValues v 
     ON v.ScenarioId = i.ScenarioId 
     AND v.PropertyAssetID = i.PropertyAssetID 
     AND v.FinancialLineItemId = i.Id 
    JOIN rvw_Portfolios po ON po.Id = sc.portfolioid 
    JOIN Accounts ac 
     ON ac.ChartOfAccountId = po.ChartOfAccountId 
     AND ac.AccountNumber = i.AccountNumber 
    JOIN fn_LookupClassTypes() cl On cl.Id = ac.ClassTypeId 
    JOIN LineItemTypes ty On ty.Id = ac.LineItemTypeId 
    Left JOIN OtherRevenues r 
     ON r.PropertyAssetID = i.PropertyAssetID 
     AND r.AccountID = i.AccountNumber 
     AND v.[Date] BETWEEN r.[Begin] AND r.[End] 

WHERE i.ScenarioId = @scenarioid 
    and ac.HasSubAccounts = 0 
    and sc.PortfolioId = @portfolioid 
    and IsNull(r.IsMemo, 0) = 0) 
    and v.ResultSet In (0, 4) 
    and i.AccountNumber not in 
      ('401XX', '401ZZ','403ZZ','40310','41110', 
      '42010','41510','40190','40110') 
    and v.[Date] BETWEEN @fromdate AND 
      CASE WHEN pa.AnalysisEnd < @todate 
       THEN pa.AnalysisEnd ELSE @todate END 
    and ((i.LineItemTypeId = 3002 and i.BusinessEntityTypeId = 11) OR 
      (i.ModeledEntityKey = 1 and i.LineItemTypeId IN 
       (2005, 2010, 2003, 2125, 2209, 5012, 6001))) 

GROUP BY pa.AnalysisBegin,pa.Id, pa.ExternalID, i.AccountNumber, 
     ac.[Description],cl.Name,ty.Name,v.[Date] 
HAVING SUM(v.amount) <> 0 
1

je chercherais à ce qui suit d'abord. Quels sont les types d'attente pertinents pour votre procédure stockée ici? Est-ce que vous voyez beaucoup de temps de disque? Est-ce que les choses sont faites en mémoire? Il y a peut-être une latence de réseau qui tire autant d'informations.

Ensuite, à quoi ressemble le plan pour la procédure, où est-ce que tout le travail est fait?

Les points de vue pourraient certainement être un problème que vous avez mentionné. Vous pourriez avoir des tables pré-traitées pour ne pas avoir à faire autant de jointures. Spécifiquement les jointures où vous voyez le plus de CPU dépensé.

1

Les sous-requêtes corrélées sont généralement lentes et ne doivent jamais être utilisées lorsque vous tentez d'obtenir des performances. Utilisez la commande fn_split pour créer une table temporaire. Indexez-la si nécessaire, puis joignez-la pour obtenir la valeur dont vous avez besoin. Vous devrez peut-être vous joindre plusieurs fois pour une valeur différente, sans connaître les données que j'ai du mal à visualiser.

Il est également mauvais pour les performances d'utiliser OU. Utilisez UNION ALL dans une table dérivée à la place.

Étant donné que vous avez toutes ces conditions dans la vue rvw_FinancialLineItems, oui, il peut être utile de les extraire vers une table temporaire, puis d'indexer la table temporaire.

Vous pourriez aussi voir si l'utilisation des vues est une bonne idée. Souvent, les vues ont des jointures avec plusieurs tables dont vous ne recevez pas de données et sont donc moins performantes que l'interrogation uniquement des tables dont vous avez réellement besoin. Cela est particulièrement vrai si votre organisation était assez bête pour créer des vues qui appellent des vues.