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>给存储的过程?还是有更好的方法来做到这一点?

非常感谢,布雷特



什么版本的SQL Server?2005年??2008年 2008 R2 ?? SQL Server 2008和较新的具有“表值参数”(见Redth对细节响应)的概念
marc_s

不相关的提示:SqlDataReader也是IDisposable的,因此应放在一个using块中。
查迪西莫(Richardissimo)

Answers:


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

2
我们尝试过,但我们发现它的缺点而不是由Enitiy框架支持
Bishoy汉娜

7
如果您在LINQ2SQL中使用,请谨慎使用此解决方案,因为它不支持用户定义的表类型作为参数!可以在Jon Raynor的答案中找到一种解决方法,使用逗号分隔的列表和解析器功能,但这也有缺点
...。– Fazi

3
在这种情况下,@ Fazi不要使用Linq2SQL。最好在T-SQL中进行字符串连接和解析
Panagiotis Kanavos 2015年

2
是的,那么您实际上如何从SSMS执行此操作?
Sinaesthetic

21

这种情况下的典型模式是将元素传递到以逗号分隔的列表中,然后在SQL中将其拆分成可以使用的表。大多数人通常会创建一个指定的函数来执行此操作,例如:

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

然后,您可以在其他查​​询中使用它。


17
让我抛出一个dbo.SplitCommaString实现的完整性链接:goo.gl/P9ROs
Veli Gebrev 2011年

3
当其中一个数据字段中出现逗号时,会发生什么?
凯文·潘科2014年

3
改为使用管道定界。
亚历克斯在巴黎

@AlexInParis如果您的一个数据字段中有管道,该怎么办?
RayLoveless

2
然后,使用不在您的数据字段中的内容。如果有必要,请清理您的数据,但是我所见过的数据很少使用管道。如果绝对必要,请找到其他字符,例如¤或§。
亚历克斯在巴黎

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(表值参数):SQL 2008 TVP和LINQ,如果QueryTable的结构比字符串数组更复杂,否则将显得过大,因为需要在SQl Server中创建表类型


2

用一列而不是列表制作一个数据表,并将字符串添加到表中。您可以将此数据表作为结构化类型传递,并使用表的标题字段执行另一个联接。


那是要走的路。我实际上在数据库端创建了一个表,并用bcp写入服务器。
dier



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#中,试试看

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.