Підсумок : Додавання критеріїв до WHERE
пункту та розділення запиту на чотири окремі запити, по одному для кожного поля дозволяв SQL-серверу надати паралельний план і змусив запит виконуватись 4X так швидко, як це було без додаткового тесту в WHERE
пункті. Розбиття запитів на чотири без тесту не зробило цього. Також не додавали тест, не розбиваючи запити. Оптимізація тесту скоротила загальний час роботи до 3 хвилин (з початкових 3 годин).
Для мого оригінального UDF було потрібно 3 години 16 хвилин, щоб обробити 1174 731 рядок, протестовано 1,221 ГБ даних nvarchar. Використовуючи CLR, наданий у своїй відповіді Мартіном Смітом, план виконання ще не був паралельним, а завдання займало 3 години 5 хвилин.
Прочитавши, що WHERE
критерії можуть допомогти просунути UPDATE
паралель, я зробив наступне. Я додав функцію до модуля CLR, щоб перевірити, чи відповідає поле збігу з регулярним виразом:
[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());
}
і, в internal class ReplaceSpecification
, я додав код для виконання тесту проти регулярного вираження
internal bool IsMatch(string inputString)
{
if (Regex == null)
return false;
return Regex.IsMatch(inputString);
}
Якщо всі поля тестуються в одному операторі, SQL-сервер не паралелізує роботу
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);
Час на виконання понад 4 1/2 години та все ще працює. План виконання:
Однак, якщо поля розділені на окремі оператори, використовується паралельний робочий план, і моє використання процесора переходить від 12% при послідовних планах до 100% при паралельних планах (8 ядер).
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;
Час на виконання 46 хвилин. Рядкова статистика показала, що близько 0,5% записів мали принаймні один збіг регулярних виразів. План виконання:
Тепер основним затримкою часу був WHERE
пункт. Потім я замінив тест регулярного вираження в WHERE
пункті алгоритмом Aho-Corasick, реалізованим як CLR. Це зменшило загальний час до 3 хвилин 6 секунд.
Це вимагало наступних змін. Завантажте збірку та функції алгоритму Aho-Corasick. Змініть WHERE
пункт на
WHERE InProcess = 1 AND dbo.ContainsWordsByObject(ISNULL(FieldBeingTestedGoesHere,'x'), @ac) = 1;
І додайте наступне перед першим 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
Конструкція не гарантує роботу , як ви очікуєте. Приклад підключення елемента (див. Відповідь від Microsoft). Отже, перехід на SQLCLR має додаткову перевагу в гарантуванні правильних результатів, що завжди приємно.