SQL Server - Behandeln der Lokalisierung von Zeichenfolgen in verschachtelten nicht deterministischen Ansichtsstapeln


20

Beim Erstellen eines Datenbankprofils bin ich auf eine Ansicht gestoßen , die auf einige nicht deterministische Funktionen verweist, auf die für jede Verbindung im Pool dieser Anwendung 1000 bis 2500 Mal pro Minute zugegriffen wird . Eine einfache SELECTAnsicht ergibt den folgenden Ausführungsplan:

Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben

Dies scheint ein komplexer Plan für eine Ansicht mit weniger als tausend Zeilen zu sein, in denen sich möglicherweise alle paar Monate eine oder zwei Zeilen ändern. Bei folgenden anderen Beobachtungen wird es jedoch noch schlimmer:

  1. Verschachtelte Ansichten sind nicht deterministisch, daher können wir sie nicht indizieren
  2. Jede Ansicht verweist auf mehrere UDFs, um die Zeichenfolgen zu erstellen
  3. Jede UDF enthält verschachtelte UDFs, um die ISO-Codes für lokalisierte Sprachen abzurufen
  4. Ansichten im Stapel verwenden zusätzliche String-Builder, die von UDFs als JOINPrädikate zurückgegeben werden
  5. Jede Ansicht Stapel wird als Tabelle behandelt, was bedeutet , dass es INSERT/ UPDATE/ DELETETrigger für jede zu schreiben , um die zugrunde liegenden Tabellen
  6. Diese Auslöser in den Ansichten verwenden CURSORSdie EXECgespeicherten Prozeduren, die auf mehr dieser Zeichenfolgengebäude verweisen UDF.

Dies scheint mir ziemlich faul zu sein, aber ich habe nur ein paar Jahre Erfahrung mit TSQL. Es wird auch besser!

Es scheint, dass der Entwickler, der dies für eine großartige Idee hielt, dies alles getan hat, damit die wenigen hundert gespeicherten Zeichenfolgen eine Übersetzung basierend auf einer Zeichenfolge haben können, die von einer schemaspezifischen zurückgegeben UDFwird.

Hier ist eine der Ansichten im Stapel, aber sie sind alle gleich schlecht:

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

Hier ist, warum UDFs als JOINPrädikate verwendet werden. Die I18NIDSpalte wird gebildet durch Verketten von:STRING + [ + ID + | + ID + ]

Während des Testens von diesen gibt eine einfache SELECTAnsicht ~ 309 Zeilen zurück und benötigt 900-1400 ms, um ausgeführt zu werden. Wenn ich die Zeichenfolgen in eine andere Tabelle kopiere und einen Index darauf lege, wird dieselbe Auswahl in 20 bis 75 ms zurückgegeben.

So lange Geschichte kurz (und ich hoffe , dass Sie einige dieser sillyness geschätzt) ich ein guter Samariter und Re-Design und Re-write dies für die 99% der Kunden sein wollen dieses Produkt läuft die noch nicht jede Lokalisierung bei Nur- verwenden Es wird erwartet, dass Endbenutzer das [en-US]Gebietsschema auch dann verwenden, wenn Englisch eine zweite / dritte Sprache ist.

Da dies ein inoffizieller Hack ist, denke ich an Folgendes:

  1. Erstellen Sie eine neue String-Tabelle, die mit einem sauber verknüpften Datensatz aus den ursprünglichen Basistabellen gefüllt ist
  2. Indizieren Sie die Tabelle.
  3. Erstellen Sie ein Ersatz - Set von Top-Level - Ansichten in dem Stapel , die einschließen NVARCHARund INTSpalten für die WKTypeund WKIndexSpalten.
  4. Ändern Sie eine Handvoll UDFs , die diese Ansichten zu vermeiden Typkonvertierungen in einigen Joinvergleichselemente verweisen (unsere größte Audit - Tabelle ist 500-2,000M Zeilen und speichert eine INTin einer NVARCHAR(4000)Spalte , die gegen die verbinden verwendet wird WKIndexSpalte ( INT).)
  5. Schemabind die Ansichten
  6. Fügen Sie den Ansichten einige Indizes hinzu
  7. Erstellen Sie die Auslöser in den Ansichten mithilfe der festgelegten Logik anstelle der Cursor neu

Nun meine eigentlichen Fragen:

  1. Gibt es eine Best Practice-Methode, um lokalisierte Zeichenfolgen über eine Ansicht zu verarbeiten?
  2. Welche Alternativen gibt es für die Verwendung von a UDFals Stub? (Ich kann VIEWfür jeden Schemabesitzer ein spezifisches schreiben und die Sprache hart codieren, anstatt mich auf eine Vielzahl von UDFStubs zu verlassen.)
  3. Können diese Ansichten einfach deterministisch gemacht werden, indem die verschachtelten UDFs vollständig qualifiziert und dann die Ansichtsstapel schematisiert werden?

5
Versuchen Sie, die skalare UDF in eine Inline-UDF mit Tabellenwert zu konvertieren . Veröffentlichen Sie auch Ihre UDFDefinition. Beziehen Sie sich auch auf T-SQL Benutzerdefinierte Funktionen: Zwei glorreiche Halunken
Kin Shah


Antworten:


1

Wenn wir uns den gegebenen Code ansehen, können wir sagen:

  • Erstens sollte dies keine Ansicht sein, sondern eine gespeicherte Prozedur, da nicht nur aus einer Tabelle gelesen, sondern auch UDFs verwendet werden.
  • Zweitens sollte die UDF nicht häufig für dieselbe Spalte aufgerufen werden. Hier wird es einmal in der Auswahl aufgerufen

    ,dbo.User3StringI18N_KeyValue(b.WKType, b.WKIndex, N'WKS') AS I18NID 

    und zum zweiten Mal für den Beitritt

    .IETFLangCode = dbo.User3StringI18N_KeyValue(b.WKType, b.WKIndex, N'WKS')

Sie können Werte in einer temporären Tabelle generieren oder einen CTE (Common Table Expression) verwenden, um diese Werte an erster Stelle abzurufen, bevor der Join stattfindet.

Ich habe ein USP-Beispiel generiert, das einige Verbesserungen bietet:

CREATE PROCEDURE usp_UserWKStringI18N
AS
BEGIN
    -- Do operation using UDF 
    SELECT b.WKType
        ,b.WKIndex
        ,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
    INTO #tempTable
    FROM UserWKStringBASE b;

    -- Now final Select
    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
        ,b.I18NID
        ,b.IETFSessionLangCode
        ,b.IETFDatabaseLangCode
    FROM #tempTable b
    LEFT OUTER JOIN User3StringI18N il
        ON il.I18NID = b.I18NID
            AND il.IETFLangCode = b.IETFSessionLangCode
    LEFT OUTER JOIN User3StringI18N id
        ON id.I18NID = b.I18NID
            AND id.IETFLangCode = b.IETFDatabaseLangCode
END

Bitte versuchen Sie dies


Hallo MarmiK, danke, dass du dir die Zeit genommen hast, dir diesen Beitrag anzusehen. Dies ist leider eine Ansicht (in einer Reihe von verschachtelten Ansichten), sodass das Verschieben in eine gespeicherte Prozedur nicht in Frage kam.
Beeks

In diesem Fall können wir CTE in View verwenden, da temporäre Tabellen in View nicht empfohlen werden. ODER die Zeilen der temporären Tabelle können von einer gespeicherten Prozedur generiert und in der Ansicht aufgerufen werden.
MarmiK
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.