Konkluzja : Dodanie kryteriów do WHERE
klauzuli i podzielenie zapytania na cztery osobne zapytania, po jednym dla każdego pola, pozwoliło serwerowi SQL na zapewnienie równoległego planu i spowodowało, że zapytanie uruchomiono 4 razy szybciej niż bez dodatkowego testu w WHERE
klauzuli. Podział zapytań na cztery bez testu tego nie zrobił. Nie dodano też testu bez dzielenia zapytań. Optymalizacja testu skróciła całkowity czas pracy do 3 minut (z pierwotnych 3 godzin).
Moja oryginalna funkcja UDF zajęła 3 godziny 16 minut na przetworzenie 1 177 731 wierszy i przetestowanie 1,216 GB danych nvarchar. Korzystając z CLR dostarczonego przez Martina Smitha w swojej odpowiedzi, plan wykonania wciąż nie był równoległy, a zadanie trwało 3 godziny i 5 minut.
Po zapoznaniu się z tymi WHERE
kryteriami może pomóc w doprowadzeniu UPDATE
do równoległości, zrobiłem następujące. Dodałem funkcję do modułu CLR, aby sprawdzić, czy pole ma dopasowanie do wyrażenia regularnego:
[SqlFunction(IsDeterministic = true,
IsPrecise = true,
DataAccess = DataAccessKind.None,
SystemDataAccess = SystemDataAccessKind.None)]
public static SqlBoolean CanReplaceMultiWord(SqlString inputString, SqlXml replacementSpec)
{
string s = replacementSpec.Value;
ReplaceSpecification rs;
if (!cachedSpecs.TryGetValue(s, out rs))
{
var doc = new XmlDocument();
doc.LoadXml(s);
rs = new ReplaceSpecification(doc);
cachedSpecs[s] = rs;
}
return rs.IsMatch(inputString.ToString());
}
i, w internal class ReplaceSpecification
, dodałem kod, aby wykonać test względem wyrażenia regularnego
internal bool IsMatch(string inputString)
{
if (Regex == null)
return false;
return Regex.IsMatch(inputString);
}
Jeśli wszystkie pola są testowane w jednej instrukcji, serwer SQL nie wykonuje pracy równoległej
UPDATE dbo.DeidentifiedTest
SET IndexedXml = dbo.ReplaceMultiWord(IndexedXml, @X),
DE461 = dbo.ReplaceMultiWord(DE461, @X),
DE87 = dbo.ReplaceMultiWord(DE87, @X),
DE15 = dbo.ReplaceMultiWord(DE15, @X)
WHERE InProcess = 1
AND (dbo.CanReplaceMultiWord(IndexedXml, @X) = 1
OR DE15 = dbo.ReplaceMultiWord(DE15, @X)
OR dbo.CanReplaceMultiWord(DE87, @X) = 1
OR dbo.CanReplaceMultiWord(DE15, @X) = 1);
Czas na wykonanie ponad 4 1/2 godziny i nadal działa. Plan wykonania:
Jeśli jednak pola zostaną podzielone na osobne instrukcje, zostanie użyty równoległy plan pracy, a moje użycie procesora wzrośnie z 12% w przypadku planów szeregowych do 100% w przypadku planów równoległych (8 rdzeni).
UPDATE dbo.DeidentifiedTest
SET IndexedXml = dbo.ReplaceMultiWord(IndexedXml, @X)
WHERE InProcess = 1
AND dbo.CanReplaceMultiWord(IndexedXml, @X) = 1;
UPDATE dbo.DeidentifiedTest
SET DE461 = dbo.ReplaceMultiWord(DE461, @X)
WHERE InProcess = 1
AND dbo.CanReplaceMultiWord(DE461, @X) = 1;
UPDATE dbo.DeidentifiedTest
SET DE87 = dbo.ReplaceMultiWord(DE87, @X)
WHERE InProcess = 1
AND dbo.CanReplaceMultiWord(DE87, @X) = 1;
UPDATE dbo.DeidentifiedTest
SET DE15 = dbo.ReplaceMultiWord(DE15, @X)
WHERE InProcess = 1
AND dbo.CanReplaceMultiWord(DE15, @X) = 1;
Czas na wykonanie 46 minut. Statystyki wierszy wykazały, że około 0,5% rekordów zawierało co najmniej jedno dopasowanie wyrażenia regularnego. Plan wykonania:
Teraz głównym problemem na czasie była WHERE
klauzula. Następnie zastąpiłem test wyrażenia regularnego w WHERE
klauzuli algorytmem Aho-Corasick zaimplementowanym jako CLR. Skróciło to całkowity czas do 3 minut 6 sekund.
Wymagało to następujących zmian. Załaduj zespół i funkcje dla algorytmu Aho-Corasick. Zmień WHERE
klauzulę na
WHERE InProcess = 1 AND dbo.ContainsWordsByObject(ISNULL(FieldBeingTestedGoesHere,'x'), @ac) = 1;
I dodaj następujące przed pierwszym UPDATE
DECLARE @ac NVARCHAR(32);
SET @ac = dbo.CreateAhoCorasick(
(SELECT NAMES FROM dbo.NamesMultiWord FOR XML RAW, root('root')),
'en-us:i'
);
SELECT @var = REPLACE ... ORDER BY
Budowa nie jest gwarantowane do pracy, jak można się spodziewać. Przykładowy element Connect (patrz odpowiedź Microsoft). Zatem przejście na SQLCLR ma tę dodatkową zaletę, że gwarantuje prawidłowe wyniki, co zawsze jest miłe.