相关文章推荐
失恋的牙膏  ·  Repositories - Composer·  1 月前    · 
神勇威武的地瓜  ·  担保债务凭证·  1 年前    · 
听话的手套  ·  GitHub - ...·  1 年前    · 
Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

For some reason the Sqlparameter for my IN() clause is not working. The code compiles fine, and the query works if I substitute the parameter with the actual values

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) 

You have to create one parameter for each value that you want in the IN clause.

The SQL needs to look like this:

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

So you need to create the parameters and the IN clause in the foreach loop.
Something like this (out of my head, untested):

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);
                Remember remove last comma at the end, because with example above it will be in (@userId,)
– Wolf
                Nov 17, 2013 at 9:21
                Its, very old post, but just one more update to answer. to help one more googler, ;) You can make use of xml as parameter to get similar effect of CSV and in clause Query -  SELECT userId, username  FROM Users u1 INNER JOIN @UsersID.nodes('/ID') T(col) ON u1.userId = t.col.value('.', 'int')
– 0cool
                May 3, 2017 at 11:54
                Huge warning to anyone using this. SQL Server defaults to 2100 as a maximum number of parameters.
– SixOThree
                Dec 15, 2020 at 21:21

If you are using SQL 2008, you can create a stored procedure which accepts a Table Valued Parameter (TVP) and use ADO.net to execute the stored procedure and pass a datatable to it:

First, you need to create the Type in SQL server:

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

Then, you need to write a stored procedure which accepts this type as a parameter:

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

Now from .net, you cannot use LINQ since it does not support Table Valued Parameters yet; so you have to write a function which does plain old ADO.net, takes a DataTable, and passes it to the stored procedure: I've written a generic function I use which can do this for any stored procedure as long as it takes just the one table-typed parameter, regardless of what it is;

    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;

Then you can write DAL functions which use this utility function with actual names of stored procedures; to build on the example in your question, here is what the code would look like:

    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;

Note the "connection" parameter above - I actually use this type of function in a partial DataContext class to extend LINQ DataContext with my TVP functionality, and still use the (using var context = new MyDataContext()) syntax with these methods.

This will only work if you are using SQL Server 2008 - hopefully you are and if not, this could be a great reason to upgrade! Of course in most cases and large production environments this is not that easy, but FWIW I think this is the best way of doing this if you have the technology available.

If most of your tables use the same type for their PK, could you not make a generalized reusable ID parameter UDT? Like:CREATE TYPE [dbo].[udt_IntId] AS TABLE([Id] [int] NULL) to be reused in any case where you need to do a sql IN clause on the int primary key id? – Pxtl Oct 11, 2017 at 16:11

Possible "cleaner" version:

StringBuilder B = new StringBuilder();
for (int i = 0; i < UserList.Count; i++)
     YourCommand.Parameters.AddWithValue($"@UserId{i}", UserList[i].UserId);
B.Append(String.Join(",", YourCommand.Parameters.Select(x => x.Name)));
                I don't get it. When you are doing YourCommand.Parameters.Select(x => x.Name) you have a list like: John, Mary, ... And not a list of User1,User2, User3.
– LucaCosta
                Mar 2, 2021 at 14:03
                @LucaCosta you get a +1 for the use of string interpolation and a -1 for nitpicking my "clean" code lol j/k good job :)
– mrogunlana
                Mar 3, 2021 at 17:19
  • If the user id's are in the DB, then the IN clause should be changed to a subquery, like so:

    IN (SELECT UserID FROM someTable WHERE someConditions)

  • This is a hack -- it doesn't work well with indexes, and you have to be careful it works right with your data, but I've used it successfully in the past:

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

  • +1 for your hack. Even if it probably forces a full scan, and prevents the optimiser from doing its job, it's a clever trick, that's usable with Access as well. – iDevlop Oct 15, 2012 at 13:04 @John: I have tried this: IN (@param) and then command.Parameters.AddWithValue("@param", "'a','b','c'"); but this is unsuccessful. Can you please advice on this. – Praveen Mar 19, 2013 at 14:54 @user1671639 If you always have 3 parameters, then you can use IN (@param1, @param2, @param3) and then command.Parameters.AddWithValue("@param1", "a"); command.Parameters.AddWithValue("@param2", "b"); command.Parameters.AddWithValue("@param3", "c");. If you don't always have 3 values, perhaps you should ask a new stackoverflow.com question, provide sufficient detail, and point me to the new question. I bet several people will try to answer right away. – John Pick Mar 20, 2013 at 4:47 @John:Thanks. But I got found a simple way and this works perfectly string param="'a','b','c'"; and then "SELECT * FROM table WHERE IN (" + param + ")";. Please advice whether this is a correct way or not. – Praveen Mar 20, 2013 at 14:51 @user1671639 If a, b, and c are user input, then your code is vulnerable to SQL injection attack. That's why you should use the Parameter object instead. If you want to discuss this further, please create a new stackoverflow.com question. – John Pick Mar 20, 2013 at 17:46