Grande différence de performance (1 heure à 1 minute) trouvée dans SQL. Pouvez-vous expliquer pourquoi?
-
10-07-2019 - |
Question
Les requêtes suivantes prennent respectivement 70 minutes et 1 minute sur une machine standard pour 1 million d’enregistrements. Quelles pourraient être les raisons possibles?
Requête [01:10:00]
SELECT *
FROM cdc.fn_cdc_get_net_changes_dbo_PartitionTest(
CASE WHEN sys.fn_cdc_increment_lsn(0x00)<sys.fn_cdc_get_min_lsn('dbo_PartitionTest')
THEN sys.fn_cdc_get_min_lsn('dbo_PartitionTest')
ELSE sys.fn_cdc_increment_lsn(0x00) END
, sys.fn_cdc_get_max_lsn()
, 'all with mask')
WHERE __$operation <> 1
Requête modifiée [00:01:10]
DECLARE @MinLSN binary(10)
DECLARE @MaxLSN binary(10)
SELECT @MaxLSN= sys.fn_cdc_get_max_lsn()
SELECT @MinLSN=CASE WHEN sys.fn_cdc_increment_lsn(0x00)<sys.fn_cdc_get_min_lsn('dbo_PartitionTest')
THEN sys.fn_cdc_get_min_lsn('dbo_PartitionTest')
ELSE sys.fn_cdc_increment_lsn(0x00) END
SELECT *
FROM cdc.fn_cdc_get_net_changes_dbo_PartitionTest(
@MinLSN, @MaxLSN, 'all with mask') WHERE __$operation <> 1
[Modifié]
J'ai essayé de recréer le scénario avec une fonction similaire pour voir si les paramètres sont évalués pour chaque ligne.
CREATE FUNCTION Fn_Test(@a decimal)RETURNS TABLE
AS
RETURN
(
SELECT @a Parameter, Getdate() Dt, PartitionTest.*
FROM PartitionTest
);
SELECT * FROM Fn_Test(RAND(DATEPART(s,GETDATE())))
Mais je reçois la même valeur pour la colonne "Paramètre" pour un million d'enregistrements traités en 38 secondes.
La solution
Même les fonctions scalaires déterministes sont évaluées au moins une fois par ligne. Si la même fonction scalaire déterministe apparaît plusieurs fois sur la même " ligne " avec les mêmes paramètres, je crois que ce n’est qu’alors qu’il sera évalué une fois - p. ex. dans un CASE WHEN fn_X (a, b, c) > 0 ALORS fn_X (a, b, c) SAUF 0 FIN
ou quelque chose comme ça.
Je pense que votre problème RAND est dû au fait que vous continuez à réensemencer:
Appels répétitifs de RAND () avec le la même valeur de départ retourne la même chose résultats.
Pour une connexion, si RAND () est appelé avec une valeur de départ spécifiée, tous les appels suivants de RAND () produisent résultats basés sur la graine RAND () appel. Par exemple, la requête suivante retournera toujours la même séquence des nombres.
J'ai commencé à mettre en cache les résultats des fonctions scalaires, comme vous l'avez indiqué, allant même jusqu'à précalculer les tables des résultats des fonctions scalaires et à les joindre. Il faut éventuellement faire quelque chose pour que les fonctions scalaires fonctionnent. Bien sûr que non, la meilleure option est le CLR - apparemment, ils surpassent de loin les UDF SQL. Malheureusement, je ne peux pas les utiliser dans mon environnement actuel.
Autres conseils
Dans votre première requête, vos fn_cdc_increment_lsn
et fn_cdc_get_min_lsn
sont exécutés pour chaque ligne. Deuxième exemple, une seule fois.