SQL Server - Menangani lokalisasi string dalam tumpukan tampilan non-deterministik bersarang

20

Saat membuat profil basis data, saya menemukan tampilan yang merujuk beberapa fungsi non-deterministik yang dapat diakses 1000-2500 kali per menit untuk setiap koneksi dalam kumpulan aplikasi ini. Sederhana SELECTdari tampilan menghasilkan rencana eksekusi berikut:

masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

Itu tampak seperti rencana rumit untuk tampilan yang memiliki kurang dari seribu baris yang dapat melihat satu atau dua baris berubah setiap beberapa bulan. Tapi itu menjadi lebih buruk dengan ketaatan lain berikut:

  1. Tampilan bersarang adalah non-deterministik, jadi kami tidak dapat mengindeksnya
  2. Setiap tampilan merujuk beberapa UDFs untuk membangun string
  3. Setiap UDF berisi UDFs bersarang untuk mendapatkan kode ISO untuk bahasa lokal
  4. Tampilan di tumpukan menggunakan pembangun string tambahan yang dikembalikan dari UDFs sebagai JOINpredikat
  5. Setiap tumpukan tampilan diperlakukan sebagai tabel, artinya ada INSERT/ UPDATE/ DELETEpemicu pada masing-masing untuk menulis ke tabel yang mendasarinya
  6. Pemicu ini pada pandangan menggunakan CURSORSyang EXECdisimpan prosedur yang referensi lebih dari ini tali bangunan UDFs.

Ini tampaknya sangat busuk bagi saya, tetapi saya hanya memiliki beberapa tahun pengalaman dengan TSQL. Menjadi lebih baik juga!

Tampaknya pengembang yang memutuskan bahwa ini adalah ide yang hebat, melakukan semua ini sehingga beberapa ratus string yang disimpan dapat memiliki terjemahan berdasarkan string yang dikembalikan dari UDFyang khusus skema.

Inilah salah satu tampilan di tumpukan, tetapi semuanya sama-sama buruk:

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

Inilah sebabnya mengapa UDFs digunakan sebagai JOINpredikat. The I18NIDkolom dibentuk dengan menggabungkan:STRING + [ + ID + | + ID + ]

Selama pengujian ini, sederhana SELECTdari tampilan mengembalikan ~ 309 baris, dan membutuhkan 900-1400ms untuk mengeksekusi. Jika saya membuang string ke tabel lain dan menampar indeks di atasnya, pilih kembali yang sama dalam 20-75ms.

Jadi, cerita panjang pendek (dan saya harap Anda menghargai beberapa kebodohan ini) Saya ingin menjadi orang Samaria yang baik dan mendesain ulang dan menulis ulang ini untuk 99% klien yang menjalankan produk ini yang tidak menggunakan lokalisasi sama sekali- pengguna akhir diharapkan untuk menggunakan [en-US]lokal bahkan ketika bahasa Inggris adalah bahasa 2/3 .

Karena ini adalah peretasan tidak resmi, saya memikirkan yang berikut:

  1. Buat tabel String baru yang diisi dengan set data yang digabungkan dengan rapi dari tabel dasar asli
  2. Buat indeks tabelnya.
  3. Buat set pengganti tampilan tingkat atas dalam tumpukan yang menyertakan NVARCHARdan INTkolom untuk WKTypedan WKIndexkolom.
  4. Ubah beberapa UDFs yang mereferensikan pandangan ini untuk menghindari konversi tipe dalam beberapa predikat gabungan (tabel audit terbesar kami adalah 500-2.000 juta baris dan menyimpan sebuah INTdalam NVARCHAR(4000)kolom yang digunakan untuk bergabung dengan WKIndexkolom ( INT).)
  5. Schemabind pandangan
  6. Tambahkan beberapa indeks ke tampilan
  7. Bangun kembali pemicu pada tampilan menggunakan set logika, bukan kursor

Sekarang, pertanyaan aktual saya:

  1. Apakah ada metode praktik terbaik untuk menangani string lokal melalui tampilan?
  2. Alternatif apa yang ada untuk menggunakan UDFsebuah rintisan? (Saya dapat menulis spesifik VIEWuntuk setiap pemilik skema dan meng-hard-code bahasa daripada mengandalkan berbagai UDFbertopik.)
  3. Bisakah pandangan ini dibuat secara deterministik dengan memenuhi syarat sepenuhnya pada nested UDFs dan kemudian menyusun skema tumpukan view?
lebah
sumber
5
Cobalah untuk mengubah UDF Scalar ke tabel Inline dihargai UDF . Posting UDFdefinisi Anda juga. Juga, lihat Fungsi yang Ditentukan Pengguna T-SQL: yang baik, yang buruk, dan yang jelek
Kin Shah
Apakah ini membantu Anda? stackoverflow.com/questions/316780/…
stacylaray
Atau yang ini? stackoverflow.com/questions/258483/…
stacylaray

Jawaban:

1

Melihat kode yang diberikan, kita dapat mengatakan,

  • Pertama, ini bukan tampilan tetapi harus menjadi prosedur tersimpan, karena tidak hanya membaca dari tabel, tetapi menggunakan UDF.
  • Kedua, UDF tidak harus sering dipanggil untuk kolom yang sama. Di sini, itu disebut sekali di pilih

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

    dan kedua kalinya untuk bergabung

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

Satu dapat menghasilkan nilai dalam tabel sementara atau menggunakan CTE (Common Table Expression) untuk mendapatkan nilai-nilai itu di tempat pertama sebelum bergabung terjadi.

Saya telah menghasilkan sampel USP yang akan memberikan beberapa peningkatan:

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

Silakan coba ini

MarmiK
sumber
Halo MarmiK, terima kasih telah meluangkan waktu untuk melihat posting ini. Sayangnya ini adalah tampilan (dalam serangkaian tampilan bersarang) sehingga memindahkannya ke prosedur tersimpan tidak mungkin dilakukan.
Beeks
baik dalam hal ini kita bisa menggunakan CTE dalam tampilan karena tabel temp tidak direkomendasikan dalam Tampilan. ATAU baris tabel temp dapat dihasilkan oleh beberapa prosedur tersimpan dan dapat dipanggil dalam tampilan.
MarmiK