如何将数组传递给SQL Server存储过程

Ser*_*gey 275 c# t-sql sql-server stored-procedures

如何将数组传递给SQL Server存储过程?

例如,我有一份员工清单.我想将此列表用作表,并将其与另一个表连接.但是员工列表应该作为参数从C#传递.

Aar*_*and 416

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
Run Code Online (Sandbox Code Playgroud)

现在在您的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
}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

现在您的存储过程可以是:

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

  SELECT EmployeeID = Item FROM dbo.SplitInts(@List, ','); 
END
GO
Run Code Online (Sandbox Code Playgroud)

在您的C#代码中,您只需将列表作为'1,2,3,12'...


我发现传递表值参数的方法简化了使用它的解决方案的可维护性,并且与包括XML和字符串拆分在内的其他实现相比,通常具有更高的性能.

输入是明确定义的(没有人必须猜测分隔符是逗号还是分号)并且我们没有依赖于其他处理函数,如果不检查存储过程的代码则不明显.

与涉及用户定义的XML模式而不是UDT的解决方案相比,这涉及相似数量的步骤,但根据我的经验,管理,维护和读取的代码要简单得多.

在许多解决方案中,您可能只需要一个或几个这些UDT(用户定义的类型),您可以将它们重复用于许多存储过程.与此示例一样,常见的要求是通过ID指针列表,函数名称描述了Ids应该表示的上下文,类型名称应该是通用的.

  • 我喜欢桌子参数的想法 - 从来没有想过 - 干杯.为什么值分隔符需要传递给SplitInts()函数调用. (3认同)
  • @ th1rdey3它们是隐式可选的.http://stackoverflow.com/a/18926590/61305 (3认同)

Ham*_*bar 41

根据我的经验,通过从employeeID创建分隔表达式,这个问题有一个棘手且好的解决方案.您应该只创建一个字符串表达式,如';123;434;365;'in-which 123,434并且365是一些employeeID.通过调用以下过程并将此表达式传递给它,您可以获取所需的记录.您可以轻松地将"另一个表"加入此查询中.此解决方案适用于所有版本的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
Run Code Online (Sandbox Code Playgroud)

  • 这不是一个好的解决方案,因为 Like % % 会导致对表进行扫描 (6认同)

Lev*_*i W 25

为存储过程使用表值参数.

当您从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();
}
Run Code Online (Sandbox Code Playgroud)


Fed*_*jdu 15

您需要将其作为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)
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用临时表与表连接.我们将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
Run Code Online (Sandbox Code Playgroud)


Sol*_*zky 13

上下文始终很重要,例如数组的大小复杂性.对于中小型列表,这里发布的几个答案都很好,但应该做出一些澄清:

  • 对于拆分分隔列表,基于SQLCLR的拆分器是最快的.有许多例子身边,如果你想编写自己的,或者你可以下载免费的SQL#的CLR函数库(这是我写的,但String_Split功能,和许多人一样,是完全免费).
  • 拆分基于XML的数组可能很快,但您需要使用基于属性的XML,而不是基于元素的XML(这是答案中显示的唯一类型,尽管@AaronBertrand的XML示例是最好的,因为他的代码使用的是text()XML函数.有关使用XML拆分列表的更多信息(即性能分析),请参阅Phil Factor的"使用XML将列表作为SQL Server中的参数传递".
  • 台湾居民入境许可证使用是伟大的数据被传输到proc和显示出来(假设你使用的至少是SQL Server 2008或更高版本)的预解析和强类型的表变量.但是,在大多数情况下,存储所有数据DataTable意味着在从原始集合中复制数据时复制内存中的数据.因此,使用DataTable传入TVP 的方法对于较大的数据集不能很好地工作(即,不能很好地扩展).
  • 与简单的Ints或Strings分隔列表不同,XML可以处理多个一维数组,就像TVP一样.而且就像DataTableTVP方法一样,XML不能很好地扩展,因为它需要额外考虑XML文档的开销,因为它会使内存中的数据量增加一倍以上.

所有这一切说,如果你正在使用的数据是大或不是很大,但还没有一致的增长,那么IEnumerableTVP方法是最好的选择,因为它流中的数据到SQL Server(就像DataTable方法),但不要求在内存中复制任何集合(与任何其他方法不同).我在这个答案中发布了SQL和C#代码的示例:

将字典传递给存储过程T-SQL


Pat*_*Chu 9

正如上面其他人所指出的,一种方法是将数组转换为字符串,然后在 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, ';')
Run Code Online (Sandbox Code Playgroud)

会产生:

价值
-----
  123
  456
  789
  246
   22
   33
   44
   55
   66

如果你想变得更狂热:

value
-----
  123
  456
  789
  246
   22
   33
   44
   55
   66

会给你与上面相同的结果(除了列名是“thenumber”),但排序。您可以像使用任何其他表一样使用表变量,因此您可以根据需要轻松地将它与数据库中的其他表连接起来。

请注意,您的 SQL Server 安装必须在兼容级别 130 或更高级别STRING_SPLIT()才能识别该功能。您可以使用以下查询检查您的兼容性级别:

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
Run Code Online (Sandbox Code Playgroud)

大多数语言(包括 C#)都有一个“join”函数,你可以用它从数组中创建一个字符串。

SELECT compatibility_level
FROM sys.databases WHERE name = 'yourdatabasename';
Run Code Online (Sandbox Code Playgroud)

然后将sqlparam您的参数作为参数传递给上面的存储过程。


小智 7

从 SQL Server 2016 开始,您可以将列表作为 NVARCHAR() 引入并使用 OPENJSON

DECLARE @EmployeeList nvarchar(500) = '[1,2,15]'


SELECT * 
FROM Employees
WHERE ID IN (SELECT VALUE FROM OPENJSON(@EmployeeList ))
Run Code Online (Sandbox Code Playgroud)


pra*_*een 6

在sql server中不支持数组,但有几种方法可以将集合传递给存储过程.

  1. 通过使用数据表
  2. 通过使用XML.Try以xml格式转换集合,然后将其作为输入传递给存储过程

以下链接可以帮助您

将集合传递给存储过程


Ada*_*dam 5

我一直在搜索如何将任何数组传递给sql server的所有示例和答案,而无需创建新的Table类型的麻烦,直到我发现这个linK,下面是我如何将它应用于我的项目:

- 以下代码将获取一个Array作为参数,并将--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 
Run Code Online (Sandbox Code Playgroud)

希望你喜欢它

  • @zaitsman:CLEANEST并不意味着最好或最合适.人们常常放弃灵活性和/或"适当的"复杂性和/或性能来获得"干净"的代码.这里的答案是"好的",但仅适用于小型数据集.如果传入的数组`@ s`是CSV,那么简单地拆分它就会更快(即INSERT INTO ... SELECT FROM SplitFunction).转换为XML比CLR慢,并且基于属性的XML无论如何都要快得多.这是一个简单的列表,但传递XML或TVP也可以处理复杂的数组.不确定通过避免简单的一次性"CREATE TYPE ... AS TABLE"获得了什么. (2认同)

Cha*_*ate 5

这会对你有所帮助.:)按照接下来的步骤,

  1. 打开查询设计器
  2. 复制粘贴以下代码,它将创建将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
    
    Run Code Online (Sandbox Code Playgroud)
  3. 创建以下存储过程

     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
    
    Run Code Online (Sandbox Code Playgroud)
  4. 执行此SP使用exec sp_DeleteId '1,2,3,12'此项是要删除的Id的字符串,

  5. 您将数组转换为C#中的字符串,并将其作为存储过程参数传递

    int[] intarray = { 1, 2, 3, 4, 5 };  
    string[] result = intarray.Select(x=>x.ToString()).ToArray();
    
    Run Code Online (Sandbox Code Playgroud)

     

    SqlCommand command = new SqlCommand();
    command.Connection = connection;
    command.CommandText = "sp_DeleteMultipleId";
    command.CommandType = CommandType.StoredProcedure;
    command.Parameters.Add("@Id",SqlDbType.VARCHAR).Value=result ;
    
    Run Code Online (Sandbox Code Playgroud)

这将删除多行,一切都是最好的