Existe-t-il un moyen d'empêcher les FDU scalaires dans les colonnes calculées d'empêcher le parallélisme?

29

Beaucoup a été écrit sur les périls des FDU scalaires dans SQL Server. Une recherche occasionnelle renverra des tas de résultats.

Il y a certains endroits où une UDF scalaire est la seule option, cependant.

À titre d'exemple: lorsqu'il s'agit de XML: XQuery ne peut pas être utilisé comme définition de colonne calculée. Une option documentée par Microsoft consiste à utiliser un UDF Scalar pour encapsuler votre XQuery dans un UDF Scalar, puis à l'utiliser dans une colonne calculée.

Cela a divers effets et certaines solutions de contournement.

  • Exécute ligne par ligne lorsque la table est interrogée
  • Force toutes les requêtes sur la table à s'exécuter en série

Vous pouvez contourner l'exécution ligne par ligne en liant de manière schématique la fonction et en persistant la colonne calculée ou en l'indexant. Aucune de ces méthodes ne peut empêcher la sérialisation forcée des requêtes atteignant la table, même lorsque l'UDF scalaire n'est pas référencé.

Existe-t-il un moyen connu de le faire?

Erik Darling
la source

Réponses:

31

Oui si vous:

  • exécutez SQL Server 2014 ou version ultérieure; et
  • sont capables d'exécuter la requête avec l' indicateur de trace 176 actif; et
  • la colonne calculée est PERSISTED

Plus précisément, au moins les versions suivantes sont requises :

  • Mise à jour cumulative 2 pour SQL Server 2016 SP1
  • Mise à jour cumulative 4 pour SQL Server 2016 RTM
  • Mise à jour cumulative 6 pour SQL Server 2014 SP2

MAIS pour éviter un bug (ref pour 2014 , et pour 2016 et 2017 ) introduit dans ces correctifs, appliquez plutôt:

L'indicateur de trace est efficace en tant –Tqu'option de démarrage , à la fois à l'échelle globale et à la portée de la session DBCC TRACEON, et par requête avec OPTION (QUERYTRACEON)ou un guide de plan.

L'indicateur de trace 176 empêche l'expansion persistante de la colonne calculée.

La charge de métadonnées initiale effectuée lors de la compilation d'une requête apporte toutes les colonnes, pas seulement celles directement référencées. Cela rend toutes les définitions de colonnes calculées disponibles pour la correspondance, ce qui est généralement une bonne chose.

Comme effet secondaire malheureux, si l'une des colonnes chargées (calculées) utilise une fonction scalaire définie par l'utilisateur, sa présence désactive le parallélisme pour la requête entière, même lorsque la colonne calculée n'est pas réellement utilisée .

L'indicateur de trace 176 aide à cela, si la colonne est persistante, en ne chargeant pas la définition (car l'expansion est ignorée). De cette façon, une fonction scalaire définie par l'utilisateur n'est jamais présente dans l'arbre de requête de compilation, donc le parallélisme n'est pas désactivé.

Le principal inconvénient de l'indicateur de trace 176 (en plus d'être seulement légèrement documenté) est qu'il empêche également la correspondance d'expression de requête avec des colonnes calculées persistantes: si la requête contient une expression correspondant à une colonne calculée persistante, l'indicateur de trace 176 empêchera l'expression d'être remplacée par une référence à la colonne calculée.

Pour plus de détails, consultez mon article SQLPerformance.com, Colonnes calculées correctement persistantes .

Étant donné que la question mentionne XML, comme alternative à la promotion de valeurs à l'aide d'une colonne calculée et d'une fonction scalaire, vous pouvez également envisager d'utiliser un index XML sélectif, comme vous l'avez écrit dans les index XML sélectifs: pas mal du tout .

Paul White dit GoFundMonica
la source
10

En plus de l'excellent Yes # 1 de @ Paul , il existe en fait un Yes # 2 qui:

  • fonctionne aussi loin que SQL Server 2005,
  • ne nécessite pas de définir un indicateur de trace,
  • n'exige pas que la colonne calculée soit PERSISTED, et
  • (car il ne nécessite pas l'indicateur de trace 176), n'empêche pas la correspondance des expressions de requête avec les colonnes calculées persistantes

Les seuls inconvénients (pour autant que je sache) sont:

  • ne fonctionne pas sur Azure SQL Database (du moins pas encore, bien qu'il fonctionne sur Amazon RDS SQL Server ainsi que SQL Server sur Linux), et
  • est un peu en dehors de la zone de confort de nombreux DBA

Et cette option est: SQLCLR

C'est vrai. Un aspect intéressant des FDU scalaires SQLCLR est que, s'ils n'accèdent pas aux données (ni utilisateur ni système), ils n'interdisent pas le parallélisme. Et ce n'est pas seulement de la théorie ou du marketing. Même si je n'ai pas le temps (pour le moment) de rédiger un compte rendu détaillé, j'ai testé et prouvé cela.

J'ai utilisé la configuration initiale du blog suivant (j'espère que l'OP ne considère pas cela comme une source non fiable 🙃):

Jeans Bad Idea: plusieurs indices

Et effectué les tests suivants:

  1. Exécuter la requête initiale telle quelle ─⇾ Parallélisme (comme prévu)
  2. Ajout d'une colonne calculée non persistante définie comme ([c2] * [c3])─⇾ Parallélisme (comme prévu)
  3. Suppression de cette colonne calculée et ajout d'une colonne calculée non persistante faisant référence à une FDU scalaire T-SQL (créée avec SCHEMABINDING) définie comme RETURN (@First * @Second);─⇾ AUCUN parallélisme (comme prévu)
  4. Suppression de la colonne calculée UDF T-SQL et ajout d'une colonne calculée non persistante qui faisait référence à une FDU scalaire SQLCLR (essayée avec les deux IsDeterministic = trueet = false) définie comme return SqlInt32.Multiply(First, Second);─⇾ Parallélisme (woo hoo !!)

Ainsi, bien que SQLCLR ne fonctionne pas pour tout le monde, il a certainement ses avantages pour les personnes / situations / environnements qui conviennent bien. Et, en ce qui concerne cette question spécifique - l'exemple donné en ce qui concerne l'utilisation de XQuery - cela fonctionnerait certainement pour cela (et, selon ce qui est spécifiquement fait, cela pourrait même être un peu plus rapide 😎).

Solomon Rutzky
la source