由于某种原因,我的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)
答案 0 :(得分:48)
您必须为IN
子句中的每个值创建一个参数。
SQL需要如下所示:
SELECT userId, username
FROM Users
WHERE userId IN (@UserId1, @UserId2, @UserId3, ...)
因此,您需要在IN
循环中创建参数和 foreach
子句。
这样的事情(从我的头脑中,未经测试):
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++;
}
答案 1 :(得分:8)
如果您使用的是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,并将它传递给存储过程:我编写了一个我使用的泛型函数,它可以为任何存储过程执行此操作,只要它只需要一个表类型参数,无论它是什么;
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<T> to perform action on the datareader;
conn.Close();
return rowsAffected;
}
}
然后你可以编写使用这个实用程序函数的DAL函数和存储过程的实际名称;建立在您的问题中的示例,这是代码的样子:
public int usp_DoSomethingWithTableTypedParameter(List<UserID> 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类中使用这种类型的函数来扩展具有TVP功能的LINQ DataContext,并且仍然使用(使用var context = new MyDataContext())语法和这些方法
这只有在您使用SQL Server 2008时才有效 - 希望您是,如果没有,这可能是升级的一个很好的理由!当然,在大多数情况下和大型生产环境中,这并不容易,但我认为如果您拥有该技术,这是实现这一目标的最佳方式。
答案 2 :(得分:6)
可能的“清洁”版本:
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)));
答案 3 :(得分:3)
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