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 SELECTde 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
UDFs pour créer les chaînes - Chaque UDF contient des
UDFs 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
UDFs commeJOINprédicats - Chaque pile de vues est traitée comme une table, ce qui signifie qu'il y a des
INSERT/UPDATE/DELETEdéclencheurs sur chacun pour écrire dans les tables sous-jacentes - Ces déclencheurs sur les vues utilisent
CURSORSque lesEXECprocédures stockées qui référence plus de ces bâtiments chaîneUDFs.
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 UDFsché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 UDFs sont utilisés comme JOINprédicats. La I18NIDcolonne est formée en concaténant:STRING + [ + ID + | + ID + ]
Pendant le test de ces derniers, un simple à SELECTpartir 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
NVARCHARetINTcolonnes pour les colonnesWKTypeetWKIndex. - Modifiez une poignée de
UDFs 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 unINTdans uneNVARCHAR(4000)colonne qui est utilisée pour se joindre à laWKIndexcolonne (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
UDFcomme stub? (Je peux écrire un spécifiqueVIEWpour chaque propriétaire de schéma et coder en dur la langue au lieu de compter sur une variété deUDFstubs.) - Ces vues peuvent-elles être simplement rendues déterministes en qualifiant pleinement les
UDFs imbriqués et en schématisant ensuite les piles de vues?




UDFdéfinition. Reportez-vous également aux fonctions définies par l'utilisateur T-SQL: le bon, le mauvais et le laid