다음을 통해 공유


자습서: C#에서 regex(정규식)를 사용하여 문자열 검색

적용 대상: SQL Server 2019(15.x) 이상 버전

이 자습서에서는 SQL Server 언어 확장을 사용하고 입력 매개 변수로 SQL Server에서 두 개의 열(ID 및 text)과 regex(정규식)를 받는 C# 클래스를 만드는 방법을 보여 줍니다. 이 클래스는 두 개의 열을 SQL Server(ID 및 text)로 다시 반환합니다.

C# 클래스로 전송된 텍스트 열의 지정된 텍스트에 대해, 코드가 지정된 정규식이 충족됐는지 확인하고, 원래 ID와 함께 해당 텍스트를 반환합니다.

이 샘플 코드는 텍스트에 C# 또는 c#(이)라는 단어가 포함되어 있는지 확인하는 정규식이 사용됩니다.

필수 조건

이 자습서에서는 dotnet build을(를) 사용하는 명령줄 컴파일로 충분합니다.

샘플 데이터 만들기

먼저 새 데이터베이스를 만들고 testdata 테이블을 IDtext 열과 함께 채웁니다.

CREATE DATABASE csharptest
GO
USE csharptest
GO

CREATE TABLE testdata (
    [id] INT,
    [text] VARCHAR(100),
)
GO

INSERT INTO testdata(id, "text") VALUES (4, 'This sentence contains C#')
INSERT INTO testdata(id, "text") VALUES (1, 'This sentence does not')
INSERT INTO testdata(id, "text") VALUES (3, 'I love c#!')
INSERT INTO testdata(id, "text") VALUES (2, NULL)
GO

기본 클래스 만들기

이 단계에서는 RegexSample.cs라는 클래스 파일을 만들고 다음 C# 코드를 해당 파일에 복사합니다.

이 기본 클래스가 SDK를 가져옵니다. 즉 이 클래스는 첫 번째 단계에서 다운로드한 C# 파일을 검색할 수 있어야 합니다.

using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using Microsoft.Data.Analysis;
using Microsoft.SqlServer.CSharpExtension.SDK;
using System.Text.RegularExpressions;

namespace UserExecutor
{
    /// <summary>
    /// This class extends the AbstractSqlServerExtensionExecutor and uses
    /// a regular expression that checks if a text contains the word "C#" or "c#"
    /// </summary>
    public class CSharpRegexExecutor: AbstractSqlServerExtensionExecutor
    {
        /// <summary>
        /// This method overrides the Execute method from AbstractSqlServerExtensionExecutor.
        /// </summary>
        /// <param name="input">
        /// A C# DataFrame contains the input dataset.
        /// </param>
        /// <param name="sqlParams">
        /// A Dictionary contains the parameters from SQL server with name as the key.
        /// </param>
        /// <returns>
        /// A C# DataFrame contains the output dataset.
        /// </returns>
        public override DataFrame Execute(DataFrame input, Dictionary<string, dynamic> sqlParams){
            // Drop NULL values and sort by id
            //
            input = input.DropNulls().OrderBy("id");

            // Create empty output DataFrame with two columns
            //
            DataFrame output = new DataFrame(new PrimitiveDataFrameColumn<int>("id", 0), new StringDataFrameColumn("text", 0));

            // Filter text containing specific substring using regex expression
            //
            DataFrameColumn texts = input.Columns["text"];
            for(int i = 0; i < texts.Length; ++i)
            {
                if(Regex.IsMatch((string)texts[i], sqlParams["@regexExpr"]))
                {
                    output.Append(input.Rows[i], true);
                }
            }

            // Modify the parameters
            //
            sqlParams["@rowsCount"]  = output.Rows.Count;
            sqlParams["@regexExpr"] = "Success!";

            // Return output dataset as a DataFrame
            //
            return output;
        }
    }
}

DLL 파일 컴파일 및 만들기

클래스 및 종속성을 DLL 파일에 패키지합니다. RegexSample.csproj라는 .csproj 파일을 만들고 다음 코드를 해당 파일에 복사합니다.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
    <EnableDynamicLoading>true</EnableDynamicLoading>
  </PropertyGroup>
  <PropertyGroup>
    <OutputPath>$(BinRoot)/$(Configuration)/</OutputPath>
    <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.Data.Analysis" Version="0.4.0" />
  </ItemGroup>
  <ItemGroup>
    <Reference Include="Microsoft.SqlServer.CSharpExtension.SDK">
      <HintPath>[path]\Microsoft.SqlServer.CSharpExtension.dll</HintPath>
    </Reference>
  </ItemGroup>
</Project>

프로젝트 폴더로 이동하여 다음 파일을 생성하는 dotnet build를 실행합니다.

path\to\project\bin\Debug\RegexSample.dll

자세한 내용은 C# 프로젝트에서 .NET DLL 만들기를 참조하세요.

외부 언어 만들기

데이터베이스에서 외부 언어를 만들어야 합니다. 외부 언어는 데이터베이스 범위 개체입니다. 즉 C#과 같은 외부 언어를 사용하려는 각 데이터베이스마다 외부 언어를 만들어야 합니다.

  1. 확장이 포함된 .zip 파일을 만듭니다.

    Windows에서 SQL Server 설정의 일부로 .NET 확장 .zip 파일이 <SQL Server install path>\MSSQL\Binn>\dotnet-core-CSharp-lang-extension.zip 위치에 설치됩니다. 이 zip 파일에는 nativecsharpextension.dll이(가) 포함되어 있습니다.

  2. .zip 파일에서 외부 언어 dotnet을 만듭니다.

    CREATE EXTERNAL LANGUAGE [dotnet]
    FROM (
        CONTENT = N'<path>\dotnet-core-CSharp-lang-extension.zip',
        FILE_NAME = 'nativecsharpextension.dll'
    );
    GO
    

권한 설정

.NET C# 코드를 실행하려면 사용자 SID S-1-15-2-1(<LocalMachineName>\ALL APPLICATION PACKAGES)에게 \MSSQL 폴더에 대한 읽기 권한을 부여해야 합니다.

  1. 폴더를 마우스 오른쪽 단추로 클릭하고 속성>보안을 선택합니다.
  2. 편집을 선택합니다.
  3. 추가를 선택합니다.
  4. 사용자, 컴퓨터, 서비스 계정 또는 그룹 선택에서
    1. 개체 유형을 선택하고 기본 제공 보안 원칙 및 그룹이 선택되어 있는지 확인합니다.
    2. 위치를 선택하여 목록의 맨 위에 있는 로컬 컴퓨터 이름을 선택합니다.
    3. ALL APPLICATION PACKAGES를 입력하고 이름을 확인한 다음에 확인을 선택하여 추가합니다. 이름이 확인되지 않으면 위치 단계부터 다시 시작합니다. SID(시스템 식별자)는 컴퓨터에 로컬입니다.

자세한 내용은 외부 언어 만들기를 참조하세요.

외부 라이브러리 만들기

CREATE EXTERNAL LIBRARY를 사용하여 DLL 파일에 대한 외부 라이브러리를 만듭니다. SQL Server는 .dll 파일에 액세스할 수 있으며 classpath에 대한 특별 권한을 설정할 필요가 없습니다.

RegEx 코드에 대한 외부 라이브러리를 만듭니다.

CREATE EXTERNAL LIBRARY [regex.dll]
FROM (CONTENT = N'<path>\RegexSample.dll')
WITH (LANGUAGE = 'Dotnet');
GO

C# 클래스 호출

저장 프로시저 sp_execute_external_script를 호출하여 SQL Server에서 C# 코드를 호출합니다. script 매개 변수에서 호출할 libraryname;namespace.classname을 정의합니다. 라이브러리 이름을 지정하지 않고 호출할 namespace.classname을 정의할 수도 있습니다. 확장은 일치하는 namespace.classname이 있는 첫 번째 라이브러리를 찾습니다. 다음 코드에서 클래스는 UserExecutor라는 호출된 네임스페이스와 CSharpRegexExecutor라는 호출된 클래스에 속합니다.

코드는 호출할 메서드를 정의하지 않습니다. 기본값으로 Execute 메서드가 호출됩니다. 즉 SQL Server에서 클래스를 호출할 수 있으려면 SDK 인터페이스를 따르고 C# 클래스에서 Execute 메서드를 구현해야 합니다.

저장 프로시저는 입력 쿼리(입력 데이터 세트) 및 정규식을 사용하고 지정된 정규식을 충족하는 행을 반환합니다. 텍스트에 C# 또는 c#(이)라는 단어가 포함되어 있는지 확인하는 정규식 [Cc]#이(가) 사용됩니다.

DECLARE @rowsCount INT;
DECLARE @regexExpr VARCHAR(200);

SET @regexExpr = N'[Cc]#';

EXEC sp_execute_external_script @language = N'dotnet',
    @script = N'regex.dll;UserExecutor.CSharpRegexExecutor',
    @input_data_1 = N'SELECT * FROM testdata',
    @params = N'@regexExpr VARCHAR(200) OUTPUT, @rowsCount INT OUTPUT',
    @regexExpr = @regexExpr OUTPUT,
    @rowsCount = @rowsCount OUTPUT
WITH result sets((
            id INT,
            TEXT VARCHAR(100)
            ));

SELECT @rowsCount AS rowsCount, @regexExpr AS message;

결과

호출을 실행한 후 두 개의 행이 있는 결과 집합을 가져와야 합니다.

C# 샘플의 결과 스크린샷