SQL Server 저장 프로 시저에 배열을 전달하는 방법은 무엇입니까?
예를 들어 직원 목록이 있습니다. 이 목록을 테이블로 사용하고 다른 테이블과 조인하고 싶습니다. 그러나 직원 목록은 C #에서 매개 변수로 전달해야합니다.
SQL Server 저장 프로 시저에 배열을 전달하는 방법은 무엇입니까?
예를 들어 직원 목록이 있습니다. 이 목록을 테이블로 사용하고 다른 테이블과 조인하고 싶습니다. 그러나 직원 목록은 C #에서 매개 변수로 전달해야합니다.
답변:
먼저, 데이터베이스에서 다음 두 오브젝트를 작성하십시오.
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를 사용하는 경우에도 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가 나타내는 컨텍스트를 설명하고 형식 이름은 일반이어야합니다.
SELECT [colA] FROM [MyTable] WHERE [Id] IN (SELECT [Id] FROM @ListOfIds)
.
내 경험을 바탕으로 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
배열 의 크기 및 복잡성 과 같은 컨텍스트는 항상 중요 합니다. 중소 규모의 목록의 경우 여기에 게시 된 몇 가지 답변은 괜찮지 만 몇 가지 설명이 필요합니다.
text()
XML 함수 XML을 사용하여 목록을 분할하는 방법에 대한 자세한 내용 (예 : 성능 분석) 은 Phil Factor의 "XML을 사용하여 목록을 SQL Server의 매개 변수로 전달" 을 참조하십시오.DataTable
한다는 것은 데이터가 원본 컬렉션에서 복사 될 때 데이터를 메모리에 복제하는 것을 의미합니다. 따라서 DataTable
TVP를 전달 하는 방법을 사용하면 더 큰 데이터 세트에 대해 잘 작동하지 않습니다 (즉, 잘 확장되지 않음).DataTable
TVP 방법 과 마찬가지로 XML은 XML 문서의 오버 헤드를 추가로 고려해야하기 때문에 메모리의 데이터 크기를 두 배 이상 확장 할 수 있으므로 확장 성이 떨어집니다.이 모든 것을 말하지만, 사용중인 데이터가 크거나 아직 크지 않지만 꾸준히 증가하는 경우 IEnumerable
TVP 방법은 데이터와 같은 DataTable
방법 으로 SQL Server로 데이터를 스트리밍 할 때 최선의 선택 이지만 BUT은 그렇지 않습니다. 다른 방법과 달리 메모리에 컬렉션을 복제해야합니다. 이 답변에 SQL 및 C # 코드의 예를 게시했습니다.
SQL Server에서는 배열을 지원하지 않지만 컬렉션을 저장된 proc에 전달할 수있는 몇 가지 방법이 있습니다.
아래 링크가 도움이 될 수 있습니다
새로운 테이블 유형을 만드는 번거 로움없이 배열을 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
즐기 셨으면 좋겠습니다
@s
이 CSV이면 간단히 분할하는 것이 더 빠릅니다 (예 : INSERT INTO ... SELECT FROM SplitFunction). XML로 변환하는 것은 CLR보다 느리고 속성 기반 XML은 훨씬 빠릅니다. 그리고 이것은 간단한 목록이지만 XML이나 TVP로 전달하면 복잡한 배열도 처리 할 수 있습니다. 간단한 일회성을 피함으로써 얻는 것이 무엇인지 확실하지 않습니다 CREATE TYPE ... AS TABLE
.
도움이 될 것입니다. :) 다음 단계를 따르십시오.
복사 다음 코드를 그대로 붙여 넣으면 String을 Int로 변환하는 함수가 생성됩니다.
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.sp_DeleteMultipleId
@List VARCHAR(MAX)
AS
BEGIN
SET NOCOUNT ON;
DELETE FROM TableName WHERE Id IN( SELECT Id = Item FROM dbo.SplitInts(@List, ','));
END
GO
이 SP 실행이 exec sp_DeleteId '1,2,3,12'
문자열을 사용 하여 삭제하려는 ID의 문자열입니다.
C #에서 배열을 문자열로 변환하여 저장 프로 시저 매개 변수로 전달합니다
int[] intarray = { 1, 2, 3, 4, 5 };
string[] result = intarray.Select(x=>x.ToString()).ToArray();
SqlCommand command = new SqlCommand();
command.Connection = connection;
command.CommandText = "sp_DeleteMultipleId";
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add("@Id",SqlDbType.VARCHAR).Value=result ;
이렇게하면 여러 행이 삭제됩니다.
이것을 알아내는 데 오랜 시간이 걸렸으므로 누군가가 필요로하는 경우 ...
이것은 Aaron의 답변에서 SQL 2005 방법과 SplitInts 함수를 사용합니다 (항상 쉼표를 사용하기 때문에 delim 매개 변수를 제거했습니다). SQL 2008을 사용하고 있지만 유형이 지정된 데이터 세트 (XSD, TableAdapters)에서 작동하는 것을 원했고 문자열 매개 변수가 작동한다는 것을 알고 있습니다.
나는 그의 기능이 "(1,2,3)"형 절에서 작동하도록하려고했고, 직설적 인 운이 없었습니다. 그래서 임시 테이블을 먼저 만든 다음 "where in"대신 내부 조인을 수행했습니다. 다음은 사용법 예입니다. 제 경우에는 특정 성분을 포함하지 않는 요리법 목록을 원했습니다.
CREATE PROCEDURE dbo.SOExample1
(
@excludeIngredientsString varchar(MAX) = ''
)
AS
/* Convert string to table of ints */
DECLARE @excludeIngredients TABLE (ID int)
insert into @excludeIngredients
select ID = Item from dbo.SplitInts(@excludeIngredientsString)
/* Select recipies that don't contain any ingredients in our excluded table */
SELECT r.Name, r.Slug
FROM Recipes AS r LEFT OUTER JOIN
RecipeIngredients as ri inner join
@excludeIngredients as ei on ri.IngredientID = ei.ID
ON r.ID = ri.RecipeID
WHERE (ri.RecipeID IS NULL)
다른 사람들이 위에서 언급했듯이 이것을 수행하는 한 가지 방법은 배열을 문자열로 변환 한 다음 SQL Server 내에서 문자열을 분할하는 것입니다.
SQL Server 2016부터는 문자열을 분리하는 기본 제공 방법이 있습니다.
STRING_SPLIT ()
임시 테이블 (또는 실제 테이블)에 삽입 할 수있는 행 집합을 반환합니다.
DECLARE @str varchar(200)
SET @str = "123;456;789;246;22;33;44;55;66"
SELECT value FROM STRING_SPLIT(@str, ';')
산출 :
값 ----- 123 456 789 246 22 33 44 55 66
더 멋진 모습을 원한다면 :
DECLARE @tt TABLE (
thenumber int
)
DECLARE @str varchar(200)
SET @str = "123;456;789;246;22;33;44;55;66"
INSERT INTO @tt
SELECT value FROM STRING_SPLIT(@str, ';')
SELECT * FROM @tt
ORDER BY thenumber
위와 동일한 결과를 제공하지만 (열 이름이 "숫자"인 경우는 제외) 정렬됩니다. 테이블 변수를 다른 테이블처럼 사용할 수 있으므로 원하는 경우 DB의 다른 테이블과 쉽게 조인 할 수 있습니다.
STRING_SPLIT()
기능을 인식 하려면 SQL Server 설치가 호환성 수준 130 이상 이어야합니다. 다음 쿼리를 통해 호환성 수준을 확인할 수 있습니다.
SELECT compatibility_level
FROM sys.databases WHERE name = 'yourdatabasename';
C #을 포함한 대부분의 언어에는 배열에서 문자열을 만드는 데 사용할 수있는 "join"기능이 있습니다.
int[] myarray = {22, 33, 44};
string sqlparam = string.Join(";", myarray);
그런 다음 sqlparam
위의 저장 프로 시저에 매개 변수로 전달 합니다.
CREATE TYPE dumyTable
AS TABLE
(
RateCodeId int,
RateLowerRange int,
RateHigherRange int,
RateRangeValue int
);
GO
CREATE PROCEDURE spInsertRateRanges
@dt AS dumyTable READONLY
AS
BEGIN
SET NOCOUNT ON;
INSERT tblRateCodeRange(RateCodeId,RateLowerRange,RateHigherRange,RateRangeValue)
SELECT *
FROM @dt
END