SQL Server-ネストされた非決定的なビュースタック内の文字列のローカライズの処理


20

私がアクセスされる一部の非決定的関数参照しているビューに出くわしたデータベースプロファイリングしながら、毎分1000から2500回をするために、各このアプリケーションのプール内の接続を。SELECTビューからの単純な結果は、次の実行計画をもたらします。

ここに画像の説明を入力してください ここに画像の説明を入力してください ここに画像の説明を入力してください ここに画像の説明を入力してください

これは、数か月ごとに1行または2行の変更が発生する可能性がある1000行未満のビューの複雑な計画のようです。しかし、次のその他の遵守事項により悪化します。

  1. ネストされたビューは非決定的であるため、インデックスを作成できません
  2. 各ビューは複数UDFのを参照して文字列を作成します
  3. 各UDFにはUDF、ローカライズされた言語のISOコードを取得するためのネストされたsが含まれています
  4. スタック内のビューは、s から返された追加の文字列ビルダー述語として使用していますUDFJOIN
  5. 各ビュースタックはテーブルとして扱われます。つまり、基礎となるテーブルに書き込むためにそれぞれにINSERT/ UPDATE/ DELETEトリガーがあります。
  6. ビューのこれらのトリガーは、これらの文字列構築をより多く参照CURSORSするEXECストアドプロシージャを使用しますUDF

これはかなり腐っているように見えますが、TSQLの経験は数年しかありません。それも良くなります!

これは素晴らしいアイデアだと判断した開発者UDFは、スキーマ固有の文字列から返された文字列に基づいて、格納されている数百の文字列を翻訳できるように、すべてを実行したようです。

スタック内のビューの1つを次に示しますが、それらはすべて等しく劣っています。

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

UDFsがJOIN述語として使用されている理由は次のとおりです。I18NIDカラムを連結することによって形成されます。STRING + [ + ID + | + ID + ]

これらのテスト中SELECT、ビューからの単純なものは〜309行を返し、実行に900-1400msかかります。文字列を別のテーブルにダンプし、インデックスをスラップすると、同じ選択が20〜75ミリ秒で返されます。

だから、長い話を短く(そしてこの愚かさのいくらかを感謝してほしい)私は良いサマリア人になり、ローカライズをまったく使用しないこの製品を実行しているクライアントの99%のためにこれを再設計し、書き直したい- - [en-US]英語が第2/3言語である場合でも、エンドユーザーはロケールを使用する必要があります。

これは非公式のハックなので、次のことを考えています。

  1. 元の基本テーブルからのデータのきれいに結合されたセットで移入された新しい文字列テーブルを作成します
  2. テーブルにインデックスを付けます。
  3. 含まスタックの最上位レベルのビューの置換セットの作成NVARCHARINTの列WKTypeWKIndex列を。
  4. UDFこれらのビューを参照する少数のsを変更して、一部の結合述部での型変換を回避します(最大の監査テーブルは500〜2,000M行INTで、NVARCHAR(4000)列に対する結合に使用される列に格納しWKIndexます(INT))。
  5. ビューをスキーマバインドする
  6. ビューにいくつかのインデックスを追加します
  7. カーソルの代わりに設定ロジックを使用して、ビューでトリガーを再構築します

さて、私の実際の質問:

  1. ビューを介してローカライズされた文字列を処理するためのベストプラクティスの方法はありますか?
  2. UDFスタブとして使用するための選択肢はどれですか?(VIEWさまざまなUDFスタブに依存する代わりに、スキーマの所有者ごとに固有のものを記述し、言語をハードコーディングできます。)
  3. これらのビューは、ネストされたUDFsを完全に修飾してからビュースタックをスキーマバインドすることで、単純に決定論的にできますか?

5
Scalar UDFをUDFの値を持つインラインテーブルに変換してみてください。UDF定義も投稿してください。また、参照してくださいT-SQLユーザー定義関数:良い、悪い、と醜い
キン・シャー

これは何らかの形であなたを助けますか? stackoverflow.com/questions/316780/…–
stacylaray

回答:


1

指定されたコードを見ると、次のことが言えます。

  • まず、これはビューではなく、ストアドプロシージャである必要があります。テーブルから読み取るだけでなく、UDFを使用するためです。
  • 次に、同じ列に対してUDFを頻繁に呼び出さないでください。ここでは、selectで1回呼び出されます

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

    そして、2回目の参加

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

一時テーブルで値を生成するか、CTE(共通テーブル式)を使用して、結合が行われる前にそれらの値を最初に取得できます。

いくつかの改善を提供するサンプルUSPを生成しました。

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

これを試してください


こんにちはMarmiK、時間を割いてこの投稿をご覧いただきありがとうございます。これは残念ながら(一連のネストされたビューの)ビューなので、ストアドプロシージャに移動することは問題外です。
beeks

一時テーブルはViewでは推奨されないため、その場合はCTEをビューで使用できます。または、一時テーブルの行を何らかのストアドプロシージャによって生成し、ビューで呼び出すことができます。
MarmiK
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.