Наш сайт использует файлы cookie, чтобы улучшить работу сайта, повысить его эффективность и удобство
Настройки сookie
Файлы cookie, необходимые для правильной работы сайта, всегда разрешены.
Основные файлы cookie
Всегда Включено. Эти файлы cookie необходимы для использования веб-сайта и его функций. Их нельзя отключить. Они устанавливаются в ответ на ваши запросы, такие как настройка параметров конфиденциальности, вход в систему или заполнение форм.
Аналитические файлы cookie
Disabled
Эти файлы cookie нужны чтобы помочь нам понять, на сколько вам удобен наш веб-сайт и насколько эффективны наши маркетологи:) Смотрите список аналитических файлов cookie, которые мы используем.
Рекламные файлы cookie
Disabled
Эти файлы cookie предоставляют информацию рекламным компаниям с целью предоставления или ограничения персонализированной рекламы. Эта информация может быть передана другим рекламным компаниям. Смотрите список рекламных файлов cookie, которые мы используем.

Использование технологии SQL CLR для увеличения производительности функционала MS SQL

блог о bi, №1 в рунете
Крайне важно при работе с крупной аналитической системой обеспечить ее быстродействие на всех этапах работы. Однако не всегда встроенный функционал MS SQL может выполнять операции с данными оперативно.
Например, при работе с большими данными крайне не рекомендуется манипулировать ими при помощи пользовательских функций, поскольку это очень сильно замедляет работу программы. Это происходит потому что MS SQL использует теоретико-множественную парадигму программирования, то есть данный язык манипулирует с данными как с объектами множества. Он хорош в операциях извлечения данных и работой с ними как набором записей. Пользовательские функции для MS SQL представляют собой «черный ящик» и при работе с данными выполняет операции построчно.

Один из способов улучшить скорость работы таких функций – использовать технологию SQL CLR.

Она позволяет расширять функциональность SQL сервера с помощью высокоуровневых языков программирования, например C# или VB.NET, путем создания пользовательских хранимых процедур, триггеров, типов данных и функций на высокопроизводительных языках программирования. Это позволяет значительно повысить производительность и интегрировать мощные алгоритмы и операции, недоступные в рамках чистого SQL, тем самым многократно расширяя возможности SQL Server.

В данной статье, мы сравним две реализации алгоритмов разбиения строки на подстроки инструментами C# и T-SQL.

Рассмотрим реализацию алгоритма в MS SQL (Листинг 1). На вход функция принимает строку, которую будем разбивать на подстроки, и строку-разделитель. На выход функция возвращает таблицу с номером подстроки в таблице и саму подстроку.
Листинг 1. Реализация алгоритма на T-SQL
ALTER Function [dbo].[split](
   @InputText Varchar(max),
   @Delimiter Varchar(10)) 


RETURNS @Array TABLE (
   TokenID Int PRIMARY KEY IDENTITY(1,1),
   Value Varchar(max))


AS
BEGIN


   DECLARE
      @Pos Int,        
      @End Int,       
      @TextLength Int, 
      @DelimLength Int 


   SET @TextLength = DataLength(@InputText)
   IF @TextLength = 0 RETURN
   SET @Pos = 0
   SET @DelimLength = DataLength(@Delimiter)


   IF @DelimLength = 0 BEGIN 
      WHILE @Pos <= @TextLength BEGIN
         INSERT @Array (Value) VALUES (SubString(@InputText,@Pos,1))
         SET @Pos = @Pos + 1
      END
   END
   ELSE BEGIN
      -- Tack on delimiter to 'see' the last token
      SET @InputText = @InputText + @Delimiter
      -- Find the end character of the first token
      SET @End = CharIndex(@Delimiter, @InputText)
      WHILE @End > 0 BEGIN
         -- End > 0, a delimiter was found: there is a(nother) token
         INSERT @Array (Value) VALUES (SubString(@InputText, @Pos, @End - @Pos))
         -- Set next search to start after the previous token
         SET @Pos = @End + @DelimLength
         -- Find the end character of the next token
         SET @End = CharIndex(@Delimiter, @InputText, @Pos)
      END
   END
   RETURN
END
Теперь создадим аналогичную функцию в среде Visual Studio на языке C#.
Для этого в Visual Studio создаем проект базы данных SQL Server.
Затем через обозреватель решений добавляем определяемую пользователем CLR функцию (Скрин 1, 2).
Скрин 1. Проект => Добавить => Скрипт
Скрин 2. SQL CLR C# => Пользовательская функция
Теперь можно реализовывать сам алгоритм (Листинг 2).
Листинг 2. Реализация алгоритма на C#
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;


public partial class UserDefinedFunctions
{
    [SqlFunction(FillRowMethodName = "SplitStringFillRow", TableDefinition = "part NVARCHAR(MAX), ID_ORDER INT")]
    static public IEnumerator SplitString(string text, string delimiter)


    {
        int text_len = text.Length;
        int delimeter_len = delimiter.Length;
        int pos = 0;
        int count = 0;


        if (text_len == 0)
        {
            yield break;
        }


        if (delimeter_len == 0)
        {
            for(int i = 0; i < text_len; i++)
            {
                yield return new KeyValuePair<int, string>(i+1, text[i].ToString());
            }
        }
        else
        {
            text = text + delimiter;
            int end = text.IndexOf(delimiter);


            if (text.Substring(pos, end - pos + delimeter_len) == delimiter)
            {
                count += 1;
                yield return new KeyValuePair<int, string>(count, text.Substring(pos, end - pos));
                pos = end + delimeter_len;
                end = text.IndexOf(delimiter, pos);
            }


            while (end > 0)
            {
                count += 1;
                yield return new KeyValuePair<int, string>(count, text.Substring(pos, end - pos));
                pos = end + delimeter_len;
                end = text.IndexOf(delimiter, pos);


            }
        }


    }
    static public void SplitStringFillRow(object oKeyValuePair, out SqlString value, out SqlInt32 valueIndex)
    {
        KeyValuePair<int, string> keyValuePair = (KeyValuePair<int, string>)oKeyValuePair;
        valueIndex = keyValuePair.Key;
        value = keyValuePair.Value;
    }
}
Прежде чем приступить к сравнению двух алгоритмов нужно настроить интеграцию CLR функции в среду MS SQL. Для этого:

  1. В Visual Studio собираем проект
  2. В MS SQL вашей базе данных настраиваем подключение внешней сборки (Скрин 3, 4)
Скрин 3. Assemblies => New Assembly
Скрин 4. В поле Path введите путь до файла dll в вашем проекте с CLR функцией
Замечание:
Если вы работаете не на локальном сервере, то нужно будет убедиться, что владелец базы данных, записанный в master, совпадает с владельцем, записанной в вашей базе данных. В противном случае, воспользуйтесь следующей командой (Листинг 3)
Листинг 3. Скрипт для смены владельца в базе данных
DECLARE @Command VARCHAR(MAX) = 'ALTER AUTHORIZATION ON DATABASE::<databasename> TO 
[<<loginname>>]' 


SELECT @Command = REPLACE(REPLACE(@Command 
            , '<databasename>', SD.Name)
            , '<loginname>', SL.Name)
FROM master..sysdatabases SD 
JOIN master..syslogins SL ON  SD.SID = SL.SID
WHERE  SD.Name = DB_NAME()


EXEC(@Command) </loginname>
Теперь можно создавать саму функцию (Листинг 4)
Листинг 4. Скрипт для создания функции в MS SQL
CREATE FUNCTION [dbo].SplitStringCLR(@text [nvarchar](max), @delimiter [nvarchar](max))
RETURNS TABLE (
part nvarchar(max),
ID_ODER int
) WITH EXECUTE AS CALLER
AS 
EXTERNAL NAME Database2.UserDefinedFunctions.SplitString*/
Протестируем работу функций на разном объеме данных. Для удобства, все результаты запишем в таблицу.

Количество строк

на вход

Время работы

CLR функции

Время работы

T-SQL функции

10000

00:00:02
00:00:14

100000

00:00:16
00:02:06

1000000

00:02:40
00:21:32
Таким образом, мы видим, что использование технологии SQL CLR может улучшить производительность запросов и кратно ускорить время их выполнение.
Это хорошее преимущество при работе с бизнес-логикой, когда требуется быстро обработать важную информацию большого объема.