C # SQL Server-저장 프로 시저에 목록 전달


111

내 C # 코드에서 SQL Server 저장 프로 시저를 호출하고 있습니다.

using (SqlConnection conn = new SqlConnection(connstring))
{
   conn.Open();
   using (SqlCommand cmd = new SqlCommand("InsertQuerySPROC", conn))
   {
      cmd.CommandType = CommandType.StoredProcedure;

      var STableParameter = cmd.Parameters.AddWithValue("@QueryTable", QueryTable);
      var NDistanceParameter = cmd.Parameters.AddWithValue("@NDistanceThreshold", NDistanceThreshold);
      var RDistanceParameter = cmd.Parameters.AddWithValue(@"RDistanceThreshold", RDistanceThreshold);

      STableParameter .SqlDbType = SqlDbType.Structured;
      NDistanceParameter.SqlDbType = SqlDbType.Int;
      RDistanceParameter.SqlDbType = SqlDbType.Int;

      // Execute the query
      SqlDataReader QueryReader = cmd.ExecuteReader();

내 저장 프로시 저는 상당히 표준 적이지만 조인을 수행합니다 QueryTable(따라서 저장 프로 시저를 사용해야 함).

이제 List<string>매개 변수 세트 에 문자열 목록을 추가하고 싶습니다 . 예를 들어, 저장된 proc 쿼리는 다음과 같습니다.

SELECT feature 
FROM table1 t1 
INNER JOIN @QueryTable t2 ON t1.fid = t2.fid 
WHERE title IN <LIST_OF_STRINGS_GOES_HERE>

그러나 문자열 목록은 동적이며 수백 개의 길이입니다.

List<string>저장된 proc 에 문자열 목록을 전달하는 방법이 있습니까 ??? 아니면 더 좋은 방법이 있습니까?

감사합니다, 브렛



어떤 버전 의 SQL Server ?? 2005 년 ?? 2008? 2008 R2 ?? SQL Server 2008 이상에는 "테이블 값 매개 변수"라는 개념이 있습니다 (자세한 내용은 Redth의 응답 참조)
marc_s

관련없는 팁 : SqlDataReader도 IDisposable이므로 using블록에 있어야합니다 .
Richardissimo 2018

답변:


178

SQL Server 2008을 사용하는 경우 사용자 정의 테이블 유형이라는 새로운 기능이 있습니다. 다음은 사용 방법의 예입니다.

사용자 정의 테이블 유형을 만듭니다.

CREATE TYPE [dbo].[StringList] AS TABLE(
    [Item] [NVARCHAR](MAX) NULL
);

다음으로 저장 프로 시저에서 올바르게 사용해야합니다.

CREATE PROCEDURE [dbo].[sp_UseStringList]
    @list StringList READONLY
AS
BEGIN
    -- Just return the items we passed in
    SELECT l.Item FROM @list l;
END

마지막으로 C #에서 사용하는 몇 가지 SQL이 있습니다.

using (var con = new SqlConnection(connstring))
{
    con.Open();

    using (SqlCommand cmd = new SqlCommand("exec sp_UseStringList @list", con))
    {
        using (var table = new DataTable()) {
          table.Columns.Add("Item", typeof(string));

          for (int i = 0; i < 10; i++)
            table.Rows.Add("Item " + i.ToString());

          var pList = new SqlParameter("@list", SqlDbType.Structured);
          pList.TypeName = "dbo.StringList";
          pList.Value = table;

          cmd.Parameters.Add(pList);

          using (var dr = cmd.ExecuteReader())
          {
            while (dr.Read())
                Console.WriteLine(dr["Item"].ToString());
          }
         }
    }
}

SSMS에서 실행하려면

DECLARE @list AS StringList

INSERT INTO @list VALUES ('Apple')
INSERT INTO @list VALUES ('Banana')
INSERT INTO @list VALUES ('Orange')

-- Alternatively, you can populate @list with an INSERT-SELECT
INSERT INTO @list
   SELECT Name FROM Fruits

EXEC sp_UseStringList @list

10
매개 변수의 값을 설정하기 위해 데이터 테이블을 정의해야합니까? 다른 가벼운 접근 방법이 있습니까?
ca9163d9 dec.

2
우리는 그것을 시도하지만 우리는 그것의 단점은 Enitiy 프레임 워크에서 지원하지 않는되고 발견
Bishoy 한나를

7
LINQ2SQL을 사용하는 경우이 솔루션은 사용자가 정의한 테이블 유형을 매개 변수로 지원하지 않으므로주의해야합니다 !!! 해결 방법은 Jon Raynor의 답변에서 쉼표로 구분 된 목록과 파서 기능을 사용하여 찾을 수 있지만 이것에도 단점이 있습니다 ....
Fazi

3
이 경우 @Fazi는 Linq2SQL을 사용하지 마십시오. T-SQL에서 문자열 연결 및 구문 분석을 수행하는 것이 좋습니다.
Panagiotis Kanavos

2
예, 실제로 SSMS에서 어떻게 실행합니까?
Sinaesthetic

21

이 상황의 일반적인 패턴은 쉼표로 구분 된 목록의 요소를 전달한 다음 SQL에서 사용할 수있는 테이블로 분할하는 것입니다. 대부분의 사람들은 일반적으로 다음과 같은 작업을 위해 지정된 함수를 만듭니다.

 INSERT INTO <SomeTempTable>
 SELECT item FROM dbo.SplitCommaString(@myParameter)

그런 다음 다른 쿼리에서 사용할 수 있습니다.


17
나 완전성에 대한 dbo.SplitCommaString 구현에 링크 던져 보자 goo.gl/P9ROs
벨리 Gebrev

3
데이터 필드 중 하나에 쉼표가 있으면 어떻게됩니까?
Kevin Panko 2014 년

3
대신 파이프로 구분하십시오.
알렉스 파리

@AlexInParis 데이터 필드 중 하나에 파이프가 있으면 어떻게합니까?
RayLoveless

2
그런 다음 데이터 필드에없는 것을 사용하십시오. 필요한 경우 데이터를 정리하지만 파이프를 사용한 적이있는 데이터는 많지 않습니다. 꼭 필요한 경우 ¤ 또는 §과 같은 다른 문자를 찾으십시오.
Alex In Paris

9

아니요, 배열 / 목록은 SQL Server에 직접 전달할 수 없습니다.

다음 옵션을 사용할 수 있습니다.

  1. 쉼표로 구분 된 목록을 전달한 다음 SQL에서 함수를 사용하면 목록이 분할됩니다. 쉼표로 구분 된 목록은 대부분 Nvarchar ()로 전달됩니다.
  2. xml을 전달하고 SQL Server의 함수가 목록의 각 값에 대해 XML을 구문 분석합니다.
  3. 새로 정의 된 사용자 정의 테이블 유형 사용 (SQL 2008)
  4. 동적으로 SQL을 빌드하고 원시 목록을 "1,2,3,4"로 전달하고 SQL 문을 빌드합니다. 이것은 SQL 인젝션 공격에 취약하지만 작동합니다.

2

예, Stored proc 매개 변수를 VARCHAR(...) 다음 과 같이 만든 다음 쉼표로 구분 된 값을 저장 프로 시저에 전달합니다.

Sql Server 2008을 사용하는 경우 TVP ( 테이블 값 매개 변수 )를 활용할 수 있습니다 . QueryTable의 구조가 문자열 배열보다 더 복잡한 경우 SQL 2008 TVP 및 LINQ 그렇지 않으면 SQl Server 내에서 테이블 형식을 만들어야하므로 과잉이 될 수 있습니다.


2

List 대신 하나의 열로 데이터 테이블을 만들고 테이블에 문자열을 추가합니다. 이 데이터 테이블을 구조화 된 유형으로 전달하고 테이블의 제목 필드와 다른 조인을 수행 할 수 있습니다.


그게 갈 길입니다. 실제로 db 측에 테이블을 만들고 bcp write to server로로드했습니다.
dier


0

내가 아는 유일한 방법은 CSV 목록을 작성한 다음 문자열로 전달하는 것입니다. 그런 다음 SP 측에서 분할하고 필요한 모든 작업을 수행하십시오.


0
CREATE TYPE [dbo].[StringList1] AS TABLE(
[Item] [NVARCHAR](MAX) NULL,
[counts][nvarchar](20) NULL);

TYPE을 테이블로 만들고 이름을 "StringList1"로 지정합니다.

create PROCEDURE [dbo].[sp_UseStringList1]
@list StringList1 READONLY
AS
BEGIN
    -- Just return the items we passed in
    SELECT l.item,l.counts FROM @list l;
    SELECT l.item,l.counts into tempTable FROM @list l;
 End

위와 같이 프로 시저를 만들고 이름을 "UserStringList1"로 지정합니다.

String strConnection = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString.ToString();
            SqlConnection con = new SqlConnection(strConnection);
            con.Open();
            var table = new DataTable();

            table.Columns.Add("Item", typeof(string));
            table.Columns.Add("count", typeof(string));

            for (int i = 0; i < 10; i++)
            {
                table.Rows.Add(i.ToString(), (i+i).ToString());

            }
                SqlCommand cmd = new SqlCommand("exec sp_UseStringList1 @list", con);


                    var pList = new SqlParameter("@list", SqlDbType.Structured);
                    pList.TypeName = "dbo.StringList1";
                    pList.Value = table;

                    cmd.Parameters.Add(pList);
                    string result = string.Empty;
                    string counts = string.Empty;
                    var dr = cmd.ExecuteReader();

                    while (dr.Read())
                    {
                        result += dr["Item"].ToString();
                        counts += dr["counts"].ToString();
                    }

C #에서 이것을 시도하십시오

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.