[C#] SQL Server 저장 프로 시저에 배열을 전달하는 방법

SQL Server 저장 프로 시저에 배열을 전달하는 방법은 무엇입니까?

예를 들어 직원 목록이 있습니다. 이 목록을 테이블로 사용하고 다른 테이블과 조인하고 싶습니다. 그러나 직원 목록은 C #에서 매개 변수로 전달해야합니다.



답변

SQL Server 2008 이상

먼저, 데이터베이스에서 다음 두 오브젝트를 작성하십시오.

CREATE TYPE dbo.IDList
AS TABLE
(
  ID INT
);
GO

CREATE PROCEDURE dbo.DoSomethingWithEmployees
  @List AS dbo.IDList READONLY
AS
BEGIN
  SET NOCOUNT ON;

  SELECT ID FROM @List;
END
GO

이제 C # 코드에서 :

// Obtain your list of ids to send, this is just an example call to a helper utility function
int[] employeeIds = GetEmployeeIds();

DataTable tvp = new DataTable();
tvp.Columns.Add(new DataColumn("ID", typeof(int)));

// populate DataTable from your List here
foreach(var id in employeeIds)
    tvp.Rows.Add(id);

using (conn)
{
    SqlCommand cmd = new SqlCommand("dbo.DoSomethingWithEmployees", conn);
    cmd.CommandType = CommandType.StoredProcedure;
    SqlParameter tvparam = cmd.Parameters.AddWithValue("@List", tvp);
    // these next lines are important to map the C# DataTable object to the correct SQL User Defined Type
    tvparam.SqlDbType = SqlDbType.Structured;
    tvparam.TypeName = "dbo.IDList";
    // execute query, consume results, etc. here
}

SQL Server 2005

SQL Server 2005를 사용하는 경우에도 XML보다 분할 기능을 권장합니다. 먼저 함수를 작성하십시오.

CREATE FUNCTION dbo.SplitInts
(
   @List      VARCHAR(MAX),
   @Delimiter VARCHAR(255)
)
RETURNS TABLE
AS
  RETURN ( SELECT Item = CONVERT(INT, Item) FROM
      ( SELECT Item = x.i.value('(./text())[1]', 'varchar(max)')
        FROM ( SELECT [XML] = CONVERT(XML, '<i>'
        + REPLACE(@List, @Delimiter, '</i><i>') + '</i>').query('.')
          ) AS a CROSS APPLY [XML].nodes('i') AS x(i) ) AS y
      WHERE Item IS NOT NULL
  );
GO

이제 저장 프로시 저는 다음과 같습니다.

CREATE PROCEDURE dbo.DoSomethingWithEmployees
  @List VARCHAR(MAX)
AS
BEGIN
  SET NOCOUNT ON;

  SELECT EmployeeID = Item FROM dbo.SplitInts(@List, ',');
END
GO

그리고 C # 코드에서는 목록을 다음과 같이 전달하면됩니다 '1,2,3,12'


테이블 값 매개 변수를 통과하는 방법은 솔루션을 사용하는 솔루션의 유지 관리를 단순화하고 XML 및 문자열 분할을 포함한 다른 구현에 비해 성능이 향상되는 경우가 많습니다.

입력이 명확하게 정의되어 있으며 (구분자가 쉼표 또는 세미콜론인지 추측 할 필요가 없음) 저장 프로 시저의 코드를 검사하지 않으면 명확하지 않은 다른 처리 기능에 종속되지 않습니다.

UDT 대신 사용자 정의 XML 스키마와 관련된 솔루션과 비교할 때 비슷한 단계가 필요하지만 경험상 관리, 유지 관리 및 읽기가 훨씬 간단한 코드입니다.

많은 솔루션에서 많은 저장 프로 시저에 재사용하는 이러한 UDT (사용자 정의 유형) 중 하나 또는 몇 개만 필요할 수 있습니다. 이 예제와 마찬가지로 일반적인 요구 사항은 ID 포인터 목록을 통과하는 것입니다. 함수 이름은 해당 ID가 나타내는 컨텍스트를 설명하고 형식 이름은 일반이어야합니다.


답변

내 경험을 바탕으로 employeeID에서 구분 된 표현식을 작성하면이 문제에 대한 까다 롭고 멋진 솔루션이 있습니다. 당신은 같은 문자열 식을 작성해야 ';123;434;365;'하는- 123, 434그리고 365일부 employeeIDs이다. 아래 프로 시저를 호출하고이 표현식을 전달하면 원하는 레코드를 가져올 수 있습니다. “다른 테이블”을이 쿼리에 쉽게 조인 할 수 있습니다. 이 솔루션은 모든 버전의 SQL Server에 적합합니다. 또한 테이블 변수 또는 임시 테이블을 사용하는 것과 비교하여 매우 빠르고 최적화 된 솔루션입니다.

CREATE PROCEDURE dbo.DoSomethingOnSomeEmployees  @List AS varchar(max)
AS
BEGIN
  SELECT EmployeeID
  FROM EmployeesTable
  -- inner join AnotherTable on ...
  where @List like '%;'+cast(employeeID as varchar(20))+';%'
END
GO


답변

저장 프로 시저에 테이블 반환 매개 변수를 사용하십시오.

C #에서 전달하면 SqlDb.Structured 데이터 형식의 매개 변수가 추가됩니다.

여기를 참조하십시오 : http://msdn.microsoft.com/en-us/library/bb675163.aspx

예:

// Assumes connection is an open SqlConnection object.
using (connection)
{
// Create a DataTable with the modified rows.
DataTable addedCategories =
  CategoriesDataTable.GetChanges(DataRowState.Added);

// Configure the SqlCommand and SqlParameter.
SqlCommand insertCommand = new SqlCommand(
    "usp_InsertCategories", connection);
insertCommand.CommandType = CommandType.StoredProcedure;
SqlParameter tvpParam = insertCommand.Parameters.AddWithValue(
    "@tvpNewCategories", addedCategories);
tvpParam.SqlDbType = SqlDbType.Structured;

// Execute the command.
insertCommand.ExecuteNonQuery();
}


답변

XML 매개 변수로 전달해야합니다.

편집 : 내 프로젝트의 빠른 코드로 아이디어를 얻을 수 있습니다.

CREATE PROCEDURE [dbo].[GetArrivalsReport]
    @DateTimeFrom AS DATETIME,
    @DateTimeTo AS DATETIME,
    @HostIds AS XML(xsdArrayOfULong)
AS
BEGIN
    DECLARE @hosts TABLE (HostId BIGINT)

    INSERT INTO @hosts
        SELECT arrayOfUlong.HostId.value('.','bigint') data
        FROM @HostIds.nodes('/arrayOfUlong/u') as arrayOfUlong(HostId)

그런 다음 임시 테이블을 사용하여 테이블과 조인 할 수 있습니다. 데이터 무결성을 유지하기 위해 arrayOfUlong을 내장 XML 스키마로 정의했지만 반드시 그럴 필요는 없습니다. 항상 XML을 오래 사용할 수 있도록하는 빠른 코드가 있으므로 사용하는 것이 좋습니다.

IF NOT EXISTS (SELECT * FROM sys.xml_schema_collections WHERE name = 'xsdArrayOfULong')
BEGIN
    CREATE XML SCHEMA COLLECTION [dbo].[xsdArrayOfULong]
    AS N'<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="arrayOfUlong">
        <xs:complexType>
            <xs:sequence>
                <xs:element maxOccurs="unbounded"
                            name="u"
                            type="xs:unsignedLong" />
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>';
END
GO


답변

배열 의 크기복잡성 과 같은 컨텍스트는 항상 중요 합니다. 중소 규모의 목록의 경우 여기에 게시 된 몇 가지 답변은 괜찮지 만 몇 가지 설명이 필요합니다.

  • 구분 된 목록을 분할하는 경우 SQLCLR 기반 분할기가 가장 빠릅니다. 직접 작성하고 싶거나 CLR 함수 의 무료 SQL # 라이브러리 (내가 작성했지만 String_Split 함수 등은 완전히 무료입니다)를 다운로드 할 수 있습니다.
  • XML 기반 배열을 분할하는 것은 빠를 있지만 요소 기반 XML이 아닌 속성 기반 XML을 사용해야합니다 (여기서 답변에 표시된 유일한 유형이지만 @AaronBertrand의 XML 예제는 코드에서 text()XML 함수 XML을 사용하여 목록을 분할하는 방법에 대한 자세한 내용 (예 : 성능 분석) 은 Phil Factor의 “XML을 사용하여 목록을 SQL Server의 매개 변수로 전달” 을 참조하십시오.
  • 데이터가 proc에 스트리밍되고 미리 분석되고 강력한 형식의 테이블 변수로 표시되므로 TVP를 사용하는 것이 좋습니다 (최소한 SQL Server 2008 이상을 사용한다고 가정). 그러나 대부분의 경우 모든 데이터를 저장 DataTable한다는 것은 데이터가 원본 컬렉션에서 복사 될 때 데이터를 메모리에 복제하는 것을 의미합니다. 따라서 DataTableTVP를 전달 하는 방법을 사용하면 더 큰 데이터 세트에 대해 잘 작동하지 않습니다 (즉, 잘 확장되지 않음).
  • XML은 간단한 구분 된 Int 또는 문자열 목록과 달리 TVP와 마찬가지로 1 차원 이상의 배열을 처리 할 수 ​​있습니다. 그러나 DataTableTVP 방법 과 마찬가지로 XML은 XML 문서의 오버 헤드를 추가로 고려해야하기 때문에 메모리의 데이터 크기를 두 배 이상 확장 할 수 있으므로 확장 성이 떨어집니다.

이 모든 것을 말하지만, 사용중인 데이터가 크거나 아직 크지 않지만 꾸준히 증가하는 경우 IEnumerableTVP 방법은 데이터와 같은 DataTable방법 으로 SQL Server로 데이터를 스트리밍 할 때 최선의 선택 이지만 BUT은 그렇지 않습니다. 다른 방법과 달리 메모리에 컬렉션을 복제해야합니다. 이 답변에 SQL 및 C # 코드의 예를 게시했습니다.

저장 프로 시저 T-SQL에 사전 전달


답변

SQL Server에서는 배열을 지원하지 않지만 컬렉션을 저장된 proc에 전달할 수있는 몇 가지 방법이 있습니다.

  1. datatable을 사용함으로써
  2. XML을 사용하여 컬렉션을 XML 형식으로 변환 한 다음 저장 프로 시저에 입력으로 전달

아래 링크가 도움이 될 수 있습니다

저장 프로 시저에 컬렉션 전달


답변

새로운 테이블 유형을 만드는 번거 로움없이 배열을 SQL Server에 전달하는 방법에 대한 모든 예제와 답변을 검색했습니다.이 linK를 찾을 때까지 아래는 내 프로젝트에 적용하는 방법입니다.

다음 코드는 Array를 Parameter로 가져 와서 –array의 값을 다른 테이블에 삽입합니다

Create Procedure Proc1


@UserId int, //just an Id param
@s nvarchar(max)  //this is the array your going to pass from C# code to your Sproc

AS

    declare @xml xml

    set @xml = N'<root><r>' + replace(@s,',','</r><r>') + '</r></root>'

    Insert into UserRole (UserID,RoleID)
    select
       @UserId [UserId], t.value('.','varchar(max)') as [RoleId]


    from @xml.nodes('//root/r') as a(t)
END 

즐기 셨으면 좋겠습니다