Итог : добавление критериев к WHERE
предложению и разделение запроса на четыре отдельных запроса, по одному для каждого поля, позволило SQL-серверу предоставить параллельный план и ускорить выполнение запроса в 4 раза, как это было без дополнительного теста в WHERE
предложении. Разделение запросов на четыре без теста не сделало этого. Ни один не сделал добавление теста без разделения запросов. Оптимизация теста сократила общее время выполнения до 3 минут (с первоначальных 3 часов).
Моему исходному UDF потребовалось 3 часа 16 минут, чтобы обработать 1 174 731 строку, при этом было протестировано 1,216 ГБ данных 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,5 часов и все еще работает. План выполнения:
Однако, если поля разделены на отдельные операторы, используется параллельный рабочий план, и моя загрузка ЦП увеличивается с 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 имеет дополнительное преимущество, гарантирующее правильные результаты, что всегда приятно.