Lors du profilage d'une base de données, je suis tombé sur une vue qui fait référence à certaines fonctions non déterministes qui sont accessibles 1000 à 2500 fois par minute pour chaque connexion dans le pool de cette application. Un simple SELECT
de la vue donne le plan d'exécution suivant:
Cela semble être un plan complexe pour une vue comportant moins d'un millier de lignes qui peut voir une ou deux lignes changer tous les quelques mois. Mais cela empire avec les autres observances suivantes:
- Les vues imbriquées ne sont pas déterministes, nous ne pouvons donc pas les indexer
- Chaque vue fait référence à plusieurs
UDF
s pour créer les chaînes - Chaque UDF contient des
UDF
s imbriqués pour obtenir les codes ISO des langues localisées - Les vues de la pile utilisent des générateurs de chaînes supplémentaires renvoyés par
UDF
s commeJOIN
prédicats - Chaque pile de vues est traitée comme une table, ce qui signifie qu'il y a des
INSERT
/UPDATE
/DELETE
déclencheurs sur chacun pour écrire dans les tables sous-jacentes - Ces déclencheurs sur les vues utilisent
CURSORS
que lesEXEC
procédures stockées qui référence plus de ces bâtiments chaîneUDF
s.
Cela me semble assez pourri, mais je n'ai que quelques années d'expérience avec TSQL. Ça va mieux aussi!
Il semble que le développeur qui a décidé que c'était une excellente idée, ait fait tout cela pour que les quelques centaines de chaînes stockées puissent avoir une traduction basée sur une chaîne renvoyée par un UDF
schéma spécifique.
Voici l'une des vues de la pile, mais elles sont toutes également mauvaises:
CREATE VIEW [UserWKStringI18N]
AS
SELECT b.WKType, b.WKIndex
, CASE
WHEN ISNULL(il.I18NID, N'') = N''
THEN id.I18NString
ELSE il.I18nString
END AS WKString
,CASE
WHEN ISNULL(il.I18NID, N'') = N''
THEN id.IETFLangCode
ELSE il.IETFLangCode
END AS IETFLangCode
,dbo.User3StringI18N_KeyValue(b.WKType, b.WKIndex, N'WKS') AS I18NID
,dbo.UserI18N_Session_Locale_Key() AS IETFSessionLangCode
,dbo.UserI18N_Database_Locale_Key() AS IETFDatabaseLangCode
FROM UserWKStringBASE b
LEFT OUTER JOIN User3StringI18N il
ON (
il.I18NID = dbo.User3StringI18N_KeyValue(b.WKType, b.WKIndex, N'WKS')
AND il.IETFLangCode = dbo.UserI18N_Session_Locale_Key()
)
LEFT OUTER JOIN User3StringI18N id
ON (
id.I18NID = dbo.User3StringI18N_KeyValue(b.WKType, b.WKIndex,N'WKS')
AND id.IETFLangCode = dbo.UserI18N_Database_Locale_Key()
)
GO
Voici pourquoi les UDF
s sont utilisés comme JOIN
prédicats. La I18NID
colonne est formée en concaténant:STRING + [ + ID + | + ID + ]
Pendant le test de ces derniers, un simple à SELECT
partir de la vue renvoie ~ 309 lignes et prend 900-1400 ms à exécuter. Si je vide les chaînes dans une autre table et que je tape un index dessus, la même sélection revient dans 20 à 75 ms.
Alors, longue histoire courte (et j'espère que vous avez apprécié une partie de cette sillyness) Je veux être un bon samaritain et re-conception et ré-écrire pour 99% des clients exécutant ce produit qui ne pas utilisent une localisation à tout - -les utilisateurs finaux sont censés utiliser les [en-US]
paramètres régionaux même lorsque l'anglais est une deuxième / troisième langue.
Puisqu'il s'agit d'un hack non officiel, je pense à ce qui suit:
- Créer une nouvelle table String remplie d'un ensemble de données jointes à partir des tables de base d'origine
- Indexez la table.
- Créez un ensemble de remplacement de vues de niveau supérieur dans la pile qui incluent
NVARCHAR
etINT
colonnes pour les colonnesWKType
etWKIndex
. - Modifiez une poignée de
UDF
s qui font référence à ces vues pour éviter les conversions de type dans certains prédicats de jointure (notre plus grande table d'audit est de 500 à 2 000 millions de lignes et stocke unINT
dans uneNVARCHAR(4000)
colonne qui est utilisée pour se joindre à laWKIndex
colonne (INT
).) - Schemabind les vues
- Ajoutez quelques index aux vues
- Reconstruisez les déclencheurs sur les vues en utilisant la logique définie au lieu des curseurs
Maintenant, mes vraies questions:
- Existe-t-il une méthode recommandée pour gérer les chaînes localisées via une vue?
- Quelles alternatives existent pour utiliser un
UDF
comme stub? (Je peux écrire un spécifiqueVIEW
pour chaque propriétaire de schéma et coder en dur la langue au lieu de compter sur une variété deUDF
stubs.) - Ces vues peuvent-elles être simplement rendues déterministes en qualifiant pleinement les
UDF
s imbriqués et en schématisant ensuite les piles de vues?
la source
UDF
définition. Reportez-vous également aux fonctions définies par l'utilisateur T-SQL: le bon, le mauvais et le laidRéponses:
En regardant le code donné, nous pouvons dire,
Deuxièmement, l'UDF ne doit pas être appelé fréquemment pour la même colonne. Ici, il est appelé une fois dans la sélection
et deuxième fois pour rejoindre
On peut générer des valeurs dans une table temporaire ou utiliser un CTE (Common Table Expression) pour obtenir ces valeurs en premier lieu avant la jointure.
J'ai généré un exemple d'USP qui apportera quelques améliorations:
Veuillez essayer ceci
la source