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

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

例如,我有一个雇员名单。 我想用这个列表作为一个表,并与另一个表join。 但是,员工名单应该作为C#的parameter passing。

既然你已经有了一个List,我认为比XML更直接。

SQL Server 2008(或更新版本)

首先,在您的数据库中,创build以下两个对象:

CREATE TYPE dbo.EmployeeList AS TABLE ( EmployeeID INT ); GO CREATE PROCEDURE dbo.DoSomethingWithEmployees @List AS dbo.EmployeeList READONLY AS BEGIN SET NOCOUNT ON; SELECT EmployeeID FROM @List; END GO 

现在在你的C#代码中:

 DataTable tvp = new DataTable(); // define / populate DataTable from your List here using (conn) { SqlCommand cmd = new SqlCommand("dbo.DoSomethingWithEmployees", conn); cmd.CommandType = CommandType.StoredProcedure; SqlParameter tvparam = cmd.Parameters.AddWithValue("@List", tvp); tvparam.SqlDbType = SqlDbType.Structured; // execute query, consume results, etc. here } 

SQL Server 2005

如果您正在使用SQL Server 2005,我仍然build议通过XML分割函数。 首先,创build一个函数:

 CREATE FUNCTION dbo.SplitInts ( @List VARCHAR(MAX), @Delimiter VARCHAR(255) ) RETURNS TABLE AS RETURN ( SELECT Item = CONVERT(INT, Item) FROM ( SELECT Item = xivalue('(./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'

我build议您将这些选项的可维护性和性能与您select的方法进行比较。

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

当你从C#传递它时,你将添加SqlDb.Structured数据types的参数。

请参阅: 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(); } 

根据我的经验,通过从employeeID创build一个分隔expression式,对于这个问题有一个棘手的,很好的解决scheme。 您应该只创build一个stringexpression式,如';123;434;365;' 其中434365是一些职员ID。 通过调用以下过程并将此expression式传递给它,您可以获取所需的logging。 很容易,您可以将“另一张桌子”join到此查询中。 此解决scheme适用于所有版本的SQL Server。 而且,与使用表variables或临时表相比,它是非常快速和优化的解决scheme。

 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 

您需要将其作为XMLparameter passing。

编辑:从我的项目快速代码给你一个想法:

 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) 

那么你可以使用临时表来join你的表。 我们将arrayOfUlong定义为一个内置的XML模式来维护数据的完整性,但是您不必这样做。 我build议使用它,所以这里有一个快速的代码,以确保你总是得到一个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的分离器是最快的。 如果你想自己写,你可以下载免费的SQL# CLR函数库(我写的,但是String_Split函数和其他很多,都是完全免费的)。
  • 拆分基于XML的数组可能很快,但是您需要使用基于属性的XML,而不是基于元素的XML(这是答案中显示的唯一types,尽pipe@AaronBertrand的XML示例是最好的,因为他的代码使用text() XML函数有关使用XML拆分列表的更多信息(即性能分析),请查看Phil Factor的“使用XML将列表作为parameter passing给SQL Server” 。
  • 使用TVP非常好(假设您至less使用SQL Server 2008或更新版本),因为数据stream式传输到proc并显示预分析和强types为表variables。 但是,在大多数情况下,将所有数据存储在DataTable意味着将数据从原始集合中复制到内存中。 因此,使用传入TVP的DataTable方法对于较大的数据集合(即,不能很好地扩展)并不适用。
  • 与简单分隔的Ints或Strings列表不同,XML可以处理多个一维数组,就像TVP一样。 但是就像DataTable TVP方法一样,XML不能很好地扩展,因为它不仅需要将内存中的数据量加倍,而且还需要额外考虑XML文档的开销。

综上所述,如果您使用的数据很大,或者不是很大,但一直在增长,那么IEnumerable TVP方法是将数据stream式传输到SQL Server(如DataTable方法)的最佳select,但是,不要求在内存中重复收集(不像其他方法)。 我在这个答案中发布了SQL和C#代码的示例:将Dictionary <string,int>传递给存储过程T-SQL 。

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

  1. 通过使用数据表
  2. 通过使用XML.Try转换您的collectionsXML格式,然后将其作为input传递给存储过程

下面的链接可能会帮助你

将收集传递给存储过程

我一直在search所有的例子和答案如何将任何数组传递到SQL Server没有创build新的表types的麻烦,直到我发现这LINK ,下面是我如何将其应用到我的项目:

– 以下代码将获取一个数组作为参数,并将该数组的值插入到另一个表中

 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 

希望你喜欢它

这将帮助你。 :)按照下面的步骤,

  1. 打开查询devise器
  2. 复制粘贴下面的代码,它将创build将string转换为Int的函数

     CREATE FUNCTION dbo.SplitInts ( @List VARCHAR(MAX), @Delimiter VARCHAR(255) ) RETURNS TABLE AS RETURN ( SELECT Item = CONVERT(INT, Item) FROM ( SELECT Item = xivalue('(./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 
  3. 创build以下存储过程

      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 
  4. 执行此SP使用exec sp_DeleteId '1,2,3,12'这是一个你想删除的Id的string,

  5. 您将数组转换为C#中的string,并将其作为存储过程parameter passing

     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,但我想与types的数据集(XSD,TableAdapters)的作品,我知道string参数与这些工作。

我试图让他的职能在“(1,2,3)”types的条款中工作,没有运气的直接的方式。 所以我先创build了一个临时表,然后做了一个内联接而不是“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)