如何将sqlparameter传递给IN()?

由于某种原因,我的IN()子句的Sqlparameter不起作用。 代码编译很好,如果我用实际值替换参数,查询就可以工作

StringBuilder sb = new StringBuilder(); foreach (User user in UserList) { sb.Append(user.UserId + ","); } string userIds = sb.ToString(); userIds = userIds.TrimEnd(new char[] { ',' }); SELECT userId, username FROM Users WHERE userId IN (@UserIds) 

您必须为IN子句中的每个值创建一个参数。

SQL需要如下所示:

 SELECT userId, username FROM Users WHERE userId IN (@UserId1, @UserId2, @UserId3, ...) 

因此,您需要在foreach循环中创建参数 IN子句。
这样的事情(从我的头脑中,未经测试):

 StringBuilder sb = new StringBuilder(); int i = 1; foreach (User user in UserList) { // IN clause sb.Append("@UserId" + i.ToString() + ","); // parameter YourCommand.Parameters.AddWithValue("@UserId" + i.ToString(), user.UserId); i++; } 

如果您使用的是SQL 2008,则可以创建一个存储过程,该过程接受表值参数(TVP)并使用ADO.net执行存储过程并将数据表传递给它:

首先,您需要在SQL服务器中创建Type:

 CREATE TYPE [dbo].[udt_UserId] AS TABLE( [UserId] [int] NULL ) 

然后,您需要编写一个接受此类型作为参数的存储过程:

 CREATE PROCEDURE [dbo].[usp_DoSomethingWithTableTypedParameter] ( @UserIdList udt_UserId READONLY ) AS BEGIN SELECT userId, username FROM Users WHERE userId IN (SELECT UserId FROM @UserIDList) END 

现在来自.net,你不能使用LINQ,因为它不支持表值参数; 所以你必须编写一个函数,它执行普通的旧ADO.net,获取一个DataTable,并将它传递给存储过程:我编写了一个我使用的generics函数,它可以为任何存储过程执行此操作,只要它只需要一个表类型的参数,无论它是什么;

  public static int ExecStoredProcWithTVP(DbConnection connection, string storedProcedureName, string tableName, string tableTypeName, DataTable dt) { using (SqlConnection conn = new SqlConnection(connection.ConnectionString)) { SqlCommand cmd = new SqlCommand(storedProcedureName, conn); cmd.CommandType = CommandType.StoredProcedure; SqlParameter p = cmd.Parameters.AddWithValue(tableName, dt); p.SqlDbType = SqlDbType.Structured; p.TypeName = tableTypeName; conn.Open(); int rowsAffected = cmd.ExecuteNonQuery(); // or could execute reader and pass a Func to perform action on the datareader; conn.Close(); return rowsAffected; } } 

然后你可以编写使用这个实用程序函数的DAL函数和存储过程的实际名称; 在你的问题的例子的基础上,这是代码的样子:

  public int usp_DoSomethingWithTableTypedParameter(List userIdList) { DataTable dt = new DataTable(); dt.Columns.Add("UserId", typeof(int)); foreach (var userId in updateList) { dt.Rows.Add(new object[] { userId }); } int rowsAffected = ExecStoredProcWithTVP(Connection, "usp_DoSomethingWithTableTypedParameter", "@UserIdList", "udt_UserId", dt); return rowsAffected; } 

注意上面的“connection”参数 – 我实际上在部分DataContext类中使用这种类型的函数来扩展具有TVPfunction的LINQ DataContext,并且仍然使用这些方法(使用var context = new MyDataContext())语法。

这只有在你使用SQL Server 2008时才有效 – 希望你是,如果没有,这可能是升级的一个很好的理由! 当然,在大多数情况下和大型生产环境中,这并不容易,但是如果您拥有可用的技术,我认为这是实现这一目标的最佳方式。

可能的“清洁”版本:

 StringBuilder B = new StringBuilder(); for (int i = 0; i < UserList.Count; i++) YourCommand.Parameters.AddWithValue("@UserId" + i.ToString(), UserList[i].UserId); B.Append(String.Join(",", YourCommand.Parameters.Select(x => x.Name))); 

SQL Server将您的IN子句视为:

 IN ('a,b,c') 

它需要看起来像是:

 IN ('a','b','c') 

有一种更好的方法来做你想做的事情。

  • 如果用户id在DB中,那么IN子句应该更改为子查询,如下所示:

    IN (SELECT UserID FROM someTable WHERE someConditions)

  • 这是一个黑客 – 它不适用于索引,你必须小心它适用于你的数据,但我过去成功使用它:

    @UserIDs LIKE '%,' + UserID + ',%' -- also requires @UserID to begin and end with a comma