web-dev-qa-db-ja.com

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) 
28
chobo

IN句に必要な値ごとに1つのパラメーターを作成する必要があります。

SQLは次のようにする必要があります。

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

そのため、パラメータを作成する必要がありますandINループ内の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++;
}
49

SQL 2008を使用している場合、テーブル値パラメーター(TVP)を受け入れるストアドプロシージャを作成し、ADO.netを使用してストアドプロシージャを実行し、それにデータテーブルを渡すことができます。

最初に、SQLサーバーでタイプを作成する必要があります。

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を取得してストアドプロシージャに渡す関数を作成する必要があります。使用する汎用関数を記述しました。何であるかに関係なく、1つのテーブル型パラメーター。

    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;
    }

上記の「接続」パラメーターに注意してください-実際にこのタイプの関数を部分的なDataContextクラスで使用して、LINQ DataContextをTVP機能で拡張し、これらのメソッドで(var context = new MyDataContext()を使用)構文を使用します。

これは、SQL Server 2008を使用している場合にのみ機能します-うまくいけば、そうでない場合は、アップグレードする大きな理由になるかもしれません!もちろん、ほとんどの場合と大規模な本番環境では、これはそれほど簡単ではありませんが、FWIWテクノロジーを利用できる場合、これが最善の方法だと思います。

8
Dmitriy Khaykin

可能な「クリーナー」バージョン:

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)));
6
mrogunlana

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_

3
John Pick