SQL Server - обробка локалізації рядків у вкладених недетермінованих стеках перегляду


20

Профілюючи базу даних, я натрапив на думку, яка посилається на деякі недетерміновані функції, які отримують доступ 1000-2500 разів на хвилину за кожне з'єднання в пулі цього додатка. Простий SELECTз виду дає такий план виконання:

введіть тут опис зображення введіть тут опис зображення введіть тут опис зображення введіть тут опис зображення

Це здається складним планом для подання, що містить менше тисячі рядків, у яких може з’являтися зміна ряду чи двох кожні кілька місяців. Але це погіршується при наступних спостереженнях:

  1. Вкладені представлення не детерміновані, тому ми не можемо їх індексувати
  2. Кожен перегляд посилається на декілька UDFs для створення рядків
  3. Кожен UDF містить вкладені UDFs для отримання ISO-кодів для локалізованих мов
  4. Перегляди в стеку використовують додаткові конструктори рядків, повернені з UDFs як JOINпредикати
  5. Кожен стек вид розглядаються як таблиці, а це означає , що є INSERT/ UPDATE/ DELETEтригера на кожному для запису в базових таблиці
  6. Ці тригери для представлень використовують CURSORSті EXECзбережені процедури, які посилаються на більшу частину цих рядків UDF.

Це здається мені досить гнилим, але я маю лише кілька років досвіду роботи з TSQL. Так само стає краще!

Схоже, розробник вирішив, що це була чудова ідея, зробив усе це, щоб кілька сотень рядків, що зберігаються, могли мати переклад на основі рядка, повернутого з того, UDFщо стосується схеми.

Ось один із поглядів у стеці, але всі вони однаково погані:

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-1400 мс для виконання. Якщо я скидаю рядки в іншу таблицю і плескаю по ній індекс, той самий вибір повертається через 20-75 мс.

Отже, коротке оповідання (і я сподіваюся, що ви оцінили деяку цю глупоту), я хочу бути хорошим самарянином і переробляти та переробляти це для 99% клієнтів, які керують цим продуктом, які взагалі не використовують локалізацію - - Очікується, що користувачі користуватимуться [en-US]мовою навіть тоді, коли англійська мова є другою та третьою мовами.

Оскільки це неофіційний злом, я думаю про наступне:

  1. Створіть нову таблицю String, заповнену чітко з’єднаним набором даних із вихідних базових таблиць
  2. Індексуйте таблицю.
  3. Створення заміни набору уявлень верхнього рівня в стеці , які включають в себе NVARCHARі INTшпальтах WKTypeі WKIndexстовпців.
  4. Зміна декількох UDFсекунд , які посилаються на ці погляди на перетворення типів уникнути в деяких предиката (наша найбільша таблиця аудиту 500-2,000M рядки і зберігає INTв NVARCHAR(4000)колонці , яка використовується для з'єднання з відношенню до WKIndexколонку ( INT).)
  5. Схематично зв’яжіть погляди
  6. Додайте кілька покажчиків до переглядів
  7. Перебудуйте тригери на подання, використовуючи задану логіку замість курсорів

Тепер мої актуальні запитання:

  1. Чи є найкращий метод для обробки локалізованих рядків за допомогою перегляду?
  2. Які існують альтернативи для використання UDFзаглушки? (Я можу написати конкретний VIEWдля кожного власника схеми і жорсткого коду мови замість того, щоб покладатися на різні UDFзаглушки.)
  3. Чи можна просто зробити ці погляди детермінованими, повністю визначивши вкладені UDFs, а потім схематично пов'язати стеки перегляду?

5
Спробуйте перетворити скалярний UDF у вбудовану таблицю UDF . Опублікуйте також своє UDFвизначення. Також зверніться до визначених користувачем функцій T-SQL: доброго, поганого та потворного
Кін Шах

Чи допомагає це вам у будь-який спосіб? stackoverflow.com/questions/316780 / ...
stacylaray

Відповіді:


1

Дивлячись на даний код, можна сказати:

  • По-перше, це не повинно бути переглядом, але це повинна бути збережена процедура, оскільки це не просто читання з таблиці, але воно використовує UDF.
  • По-друге, UDF не слід називати часто для однієї колонки. Тут він називається один раз у виділеному

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

    і Другий раз для вступу

    .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, дякую, що знайшли час, щоб ознайомитися з цією публікацією. На жаль, це перегляд (у серії вкладених представлень), тому про переміщення його до збереженої процедури не було й мови.
beks

добре, у цьому випадку ми можемо використовувати CTE з огляду, оскільки тимчасові таблиці не рекомендуються у View. АБО рядки таблиці темп можуть бути згенеровані деякою збереженою процедурою і можуть бути викликані у перегляді.
MarmiK
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.