如何将sqlparameter传递给IN()?

cho*_*obo 30 c# sql-server ado.net

由于某种原因,我的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) 
Run Code Online (Sandbox Code Playgroud)

Chr*_*cht 53

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

SQL需要如下所示:

SELECT userId, username 
FROM Users 
WHERE userId IN (@UserId1, @UserId2, @UserId3, ...) 
Run Code Online (Sandbox Code Playgroud)

所以,你需要创建的参数,并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++;
}
Run Code Online (Sandbox Code Playgroud)

  • 记得最后删除最后一个逗号,因为上面的例子它将在(@userId,) (7认同)
  • 对任何使用此功能的人发出巨大警告。SQL Server 默认参数的最大数量为 2100。 (4认同)

Dmi*_*kin 8

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

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

CREATE TYPE [dbo].[udt_UserId] AS TABLE(
    [UserId] [int] NULL
)
Run Code Online (Sandbox Code Playgroud)

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

CREATE PROCEDURE [dbo].[usp_DoSomethingWithTableTypedParameter]
(
   @UserIdList udt_UserId READONLY
)
AS
BEGIN

        SELECT userId, username 
        FROM Users 
        WHERE userId IN (SELECT UserId FROM @UserIDList) 

END
Run Code Online (Sandbox Code Playgroud)

现在来自.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;
        }
    }
Run Code Online (Sandbox Code Playgroud)

然后你可以编写使用这个实用程序函数的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;
    }
Run Code Online (Sandbox Code Playgroud)

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

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


小智 7

可能的"清洁"版本:

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


Joh*_*ick 6

SQL Server 将您的IN子句视为:

IN ('a,b,c')
Run Code Online (Sandbox Code Playgroud)

它需要看起来像:

IN ('a','b','c')
Run Code Online (Sandbox Code Playgroud)

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

  • 如果用户 ID 在数据库中,则该IN子句应更改为子查询,如下所示:

    IN (SELECT UserID FROM someTable WHERE someConditions)

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

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

  • @user1671639 如果 a、b 和 c 是用户输入,则您的代码容易受到 SQL 注入攻击。这就是您应该使用 Parameter 对象的原因。如果您想进一步讨论这个问题,请创建一个新的 stackoverflow.com 问题。 (3认同)
  • @约翰:谢谢。但我找到了一个简单的方法,它完美地工作`string param="'a','b','c'";` 然后`"SELECT * FROM table WHERE IN (" + param + ")";` . 请建议这是否是正确的方法。 (2认同)