Могу ли я реорганизовать этот запрос, чтобы он запускался параллельно?

У меня есть запрос, который занимает около 3 часов для запуска на нашем сервере - и он не использует преимущества параллельной обработки. (около 1,15 миллиона записей в dbo.Deidentified, 300 записей в dbo.NamesMultiWord). Сервер имеет доступ к 8 ядрам.

  UPDATE dbo.Deidentified 
     WITH (TABLOCK)
  SET IndexedXml = dbo.ReplaceMultiWord(IndexedXml),
      DE461 = dbo.ReplaceMultiWord(DE461),
      DE87 = dbo.ReplaceMultiWord(DE87),
      DE15 = dbo.ReplaceMultiWord(DE15)
  WHERE InProcess = 1;

и ReplaceMultiword - это процедура, определяемая как:

SELECT @body = REPLACE(@body,Names,Replacement)
 FROM dbo.NamesMultiWord
 ORDER BY [WordLength] DESC
RETURN @body --NVARCHAR(MAX)

Позволяет ли вызов ReplaceMultiword предотвратить формирование параллельного плана? Есть ли способ переписать это, чтобы разрешить параллелизм?

ReplaceMultiword работает в порядке убывания, потому что некоторые из замен являются короткими версиями других, и я хочу, чтобы самое длинное совпадение было успешным.

Например, может быть «Университет Джорджа Вашингтона», а другой - «Вашингтонский университет». Если бы матч «Вашингтонского университета» был первым, тогда «Джордж» остался позади.

 план запроса

Технически я могу использовать CLR, я просто не знаком с тем, как это сделать.

12 голосов | спросил rsjaffe 8 MaramWed, 08 Mar 2017 00:40:33 +03002017-03-08T00:40:33+03:0012 2017, 00:40:33

1 ответ


11

UDF предотвращает параллелизм. Это также вызывает эту катушку.

Вы можете использовать CLR и скомпилированное регулярное выражение для поиска и замены. Он не блокирует параллелизм as если требуемые атрибуты присутствуют и, вероятно, будут значительно быстрее, чем выполнение 300 операций TSQL REPLACE для вызова функции.

Пример кода ниже.

DECLARE @X XML = 
(
    SELECT Names AS [@find],
           Replacement  AS [@replace]
    FROM  dbo.NamesMultiWord 
    ORDER BY [WordLength] DESC
    FOR XML PATH('x'), ROOT('spec')
);

UPDATE dbo.Deidentified WITH (TABLOCK)
SET    IndexedXml = dbo.ReplaceMultiWord(IndexedXml, @X),
       DE461 = dbo.ReplaceMultiWord(DE461, @X),
       DE87 = dbo.ReplaceMultiWord(DE87, @X),
       DE15 = dbo.ReplaceMultiWord(DE15, @X)
WHERE  InProcess = 1; 

Это зависит от существования CLR UDF, как показано ниже (DataAccessKind.None означает, что катушка исчезает, а также Защита Хэллоуина и не нужна, так как это не позволяет получить доступ к целевой таблице).

 using System;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Xml;

public partial class UserDefinedFunctions
{
    //TODO: Concurrency?
    private static readonly Dictionary<string, ReplaceSpecification> cachedSpecs = 
                        new Dictionary<string, ReplaceSpecification>();

    [SqlFunction(IsDeterministic = true,
                 IsPrecise = true,
                 DataAccess = DataAccessKind.None,
                 SystemDataAccess = SystemDataAccessKind.None)]
    public static SqlString ReplaceMultiWord(SqlString inputString, SqlXml replacementSpec)
    {
        //TODO: Implement something to drop things from the cache and use a shorter key.
        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;
        }

        string result = rs.GetResult(inputString.ToString());
        return new SqlString(result);
    }


    internal class ReplaceSpecification
    {
        internal ReplaceSpecification(XmlDocument doc)
        {
            Replacements = new Dictionary<string, string>();

            XmlElement root = doc.DocumentElement;
            XmlNodeList nodes = root.SelectNodes("x");

            string pattern = null;
            foreach (XmlNode node in nodes)
            {
                if (pattern != null)
                    pattern = pattern + "|";

                string find = node.Attributes["find"].Value.ToLowerInvariant();
                string replace = node.Attributes["replace"].Value;
                 //TODO: Escape any special characters in the regex syntax
                pattern = pattern + find;
                Replacements[find] = replace;
            }

            if (pattern != null)
            {
                pattern = "(?:" + pattern + ")";
                Regex = new Regex(pattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);
            }


        }
        private Regex Regex { get; set; }

        private Dictionary<string, string> Replacements { get; set; }


        internal string GetResult(string inputString)
        {
            if (Regex == null)
                return inputString;

            return Regex.Replace(inputString,
                                 (Match m) =>
                                 {
                                     string s;
                                     if (Replacements.TryGetValue(m.Value.ToLowerInvariant(), out s))
                                     {
                                         return s;
                                     }
                                     else
                                     {
                                         throw new Exception("Missing replacement definition for " + m.Value);
                                     }
                                 });
        }
    }
}
ответил Martin Smith 8 MaramWed, 08 Mar 2017 01:26:25 +03002017-03-08T01:26:25+03:0001 2017, 01:26:25

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132