Podczas profilowania bazy danych natknąłem się na widok odwołujący się do niektórych niedeterministycznych funkcji, do których dostęp uzyskuje się 1000–2500 razy na minutę dla każdego połączenia w puli tej aplikacji. Prosty SELECT
z widoku daje następujący plan wykonania:
To wydaje się być złożonym planem dla widoku, który ma mniej niż tysiąc wierszy, w których rząd lub dwa zmieniają się co kilka miesięcy. Ale pogarsza się z następującymi innymi obserwacjami:
- Widoki zagnieżdżone są niedeterministyczne, więc nie możemy ich indeksować
- Każdy widok odwołuje się do wielu
UDF
s, aby zbudować ciągi - Każdy UDF zawiera zagnieżdżone
UDF
s, aby uzyskać kody ISO dla zlokalizowanych języków - Widoki na stosie wykorzystują dodatkowe konstruktory ciągów zwrócone zs
UDF
jakoJOIN
predykaty - Każdy stos widoków jest traktowany jak tabela, co oznacza, że na każdym są
INSERT
/UPDATE
/DELETE
wyzwalacze do zapisu do tabel bazowych - Te wyzwalacze na widokach używać
CURSORS
, żeEXEC
procedury stosowane przez odniesienie więcej z nich ciąg budynek przechowywaneUDF
s.
Wydaje mi się to dość zepsute, ale mam tylko kilka lat doświadczenia z TSQL. To też staje się lepsze!
Wygląda na to, że programista, który zdecydował, że to świetny pomysł, zrobił to wszystko, aby kilkaset zapisanych ciągów mogło mieć tłumaczenie na podstawie ciągu zwróconego z określonego UDF
schematu.
Oto jeden z widoków na stosie, ale wszystkie są równie złe:
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
Oto dlaczego UDF
są używane jako JOIN
predykaty. I18NID
Kolumna jest utworzona przez złączenie:STRING + [ + ID + | + ID + ]
Podczas testowania, prosty SELECT
z widoku zwraca ~ 309 wierszy i zajmuje 900-1400 ms do wykonania. Jeśli zrzucę ciągi znaków do innej tabeli i uderzę w nią indeksem, ten sam wybór zostanie zwrócony za 20-75 ms.
Krótko mówiąc (i mam nadzieję, że doceniliście trochę tej głupoty) Chcę być dobrym Samarytaninem i przeprojektować i napisać to dla 99% klientów korzystających z tego produktu, którzy w ogóle nie używają żadnej lokalizacji - - użytkownicy końcowi powinni korzystać z [en-US]
ustawień regionalnych, nawet jeśli angielski jest językiem 2/3.
Ponieważ jest to nieoficjalny hack, myślę o następujących rzeczach:
- Utwórz nową tabelę ciągów wypełnioną czysto połączonym zestawem danych z oryginalnych tabel podstawowych
- Indeksuj tabelę.
- Utwórz zestaw zastępczy widoków najwyższego poziomu na stosie, który zawiera
NVARCHAR
iINT
kolumny dla kolumnWKType
iWKIndex
. - Modyfikować garść
UDF
s, które odwołują się do tych poglądów typu uniknąć konwersji w niektórych dołączyć predykaty (nasz największy stół audytu jest 500-2,000M wierszy i przechowujeINT
wNVARCHAR(4000)
kolumnie, który jest używany do przyłączenia przeciwkoWKIndex
kolumnie (INT
)). - Schemabind the views
- Dodaj kilka indeksów do widoków
- Odbuduj wyzwalacze w widokach za pomocą ustawionej logiki zamiast kursorów
Teraz moje aktualne pytania:
- Czy istnieje metoda najlepszych praktyk do obsługi zlokalizowanych ciągów za pomocą widoku?
- Jakie są alternatywy dla zastosowania kodu pośredniczącego
UDF
? (Mogę napisać konkretnyVIEW
dla każdego właściciela schematu i na stałe napisać język zamiast polegać na różnych kodach pośredniczącychUDF
). - Czy widoki te można po prostu uczynić deterministycznymi, w pełni kwalifikując zagnieżdżone
UDF
s, a następnie schematyzując stosy widoków?
UDF
definicję. Zobacz też Funkcje zdefiniowane przez użytkownika T-SQL: dobre, złe i brzydkieOdpowiedzi:
Patrząc na dany kod, możemy powiedzieć:
Po drugie, UDF nie powinien być często wywoływany dla tej samej kolumny. Tutaj nazywa się to raz w zaznaczeniu
i drugi raz za dołączenie
Można wygenerować wartości w tabeli tymczasowej lub użyć CTE (Common Table Expression), aby uzyskać te wartości w pierwszej kolejności przed połączeniem.
Wygenerowałem przykładowy USP, który zapewni pewne ulepszenia:
Spróbuj tego
źródło