21

.NETコネクタのMySqlParameterについてこの質問があります。

私はこのクエリを持っています:

SELECT * FROM table WHERE id IN (@parameter)

そして、MySqlParameterは次のとおりです。

intArray = new List<int>(){1,2,3,4};

...connection.Command.Parameters.AddWithValue("parameter", intArray);

これは可能ですか?intの配列を単一のMySqlParameterに渡すことは可能ですか?もう1つの解決策は、intの配列を「1,2,3,4」などの文字列に変換することですが、これをMySqlParameterに渡して文字列として認識されると、次のようなSQLクエリが実行されます。 "1 \、2 \、3 \、4"であり、これは期待値を返しません。

@更新:mysqlコネクタチームはもう少し頑張る必要があるようです。

4

9 に答える 9

17

MySqlParameterに渡すと、これが文字列として認識されると、「1 \、2 \、3 \、4」のようなSQLクエリが実行され、期待値が返されません。

私は昨夜このことに出くわした。FIND_IN_SETがここで機能することがわかりました。

SELECT * FROM table WHERE FIND_IN_SET(id, @parameter) != 0
...
intArray = new List<int>(){1,2,3,4};
conn.Command.Parameters.AddWithValue("parameter", string.Join(",", intArray));

どうやらこれにはいくつかの長さの制限があります(私はあなたの投稿が別の解決策を探しているのを見つけました)が、これはあなたのために働くかもしれません。

于 2011-04-19T20:34:33.420 に答える
5

パラメータはINでは機能しません。私はいつもクエリ自体に文字列などを埋め込んでいます。SQLインジェクションのため、これは一般的に悪い形式と見なされますが、強く型付けされた数値リストからクエリを作成している場合は、外部入力によって意味のある方法でクエリが破損する可能性はありません。

于 2011-04-15T19:22:54.237 に答える
3

配列を反復処理して、自分でリストを作成する必要があります

// no parameters
var sb = new StringBuilder();
for(int i=0;i<intArray.Length;i++)
{
    sb.Append(intArray[i] + ",");// no SQL injection they are numbers
}
if (sb.Length>0) {sb.Length-=1;}
string sql = "SELECT * FROM table WHERE id IN (" + sb.ToString() + ")";

更新:これについてもっと考えたので、パラメーターを使用するという元の答え(以下)に戻ります。構築されたクエリの最適化と、データベースエンジンが集めることができるものは何でもあなた次第です。

// no parameters
var sb = new StringBuilder();
for(int i=0;i<intArray.Length;i++)
{
    sb.AppendFormat("p{0},", i);// no SQL injection they are numbers
    connection.Command.Parameters.AddWithValue("p"+i, intArray[i]);
}
if (sb.Length>0) {sb.Length-=1;}
string sql = "SELECT * FROM table WHERE id IN (" + sb.ToString() + ")";
于 2011-04-15T19:24:31.700 に答える
3

ここにはいくつかのオプションがあります(優先順に):

  1. テーブル値パラメーターをサポートするデータベースを使用します。これは、必要な構文を正確に取得する唯一の方法です。
  2. データは、データベース、ユーザーアクション、またはマシンで生成されたソースのいずれかから取得する必要があります。

    • データがすでにデータベースにある場合は、代わりにサブクエリを使用してください。
    • 他のマシンで生成されたデータについては、BULK INSERT、SqlBulkCopy、またはデータベースで推奨される一括インポートツールを使用してください。
    • ユーザーが作成した場合は、個々のユーザーアクションごとに個別のテーブルに追加してから、サブクエリを使用します。

      この例はショッピングカートです。ユーザーは、購入するアイテムをいくつか選択する場合があります。これらをアプリに保持し、チェックアウト時にすべてのアイテムを一度に注文に追加する必要はなく、ユーザーが選択または変更したときに、各アイテムをデータベース内のテーブルに追加します。

  3. 文字列パラメーターをテーブルに解凍し、そのテーブルをIN()式で使用できるセットとして返すsqlユーザー定義関数を用意します。これがどのように機能するかの詳細については、以下のリンクされた記事を参照してください。
  4. クライアント上で文字列リストまたはパラメータリストを動的に作成します(他の回答に示されているように)。作成するコードはSQLインジェクションの問題に対してクレイジーで脆弱である傾向があるため、これは私の最も好ましくないオプションであることに注意してください。

この主題に関する決定的な(そして私は決定的な)作業はここにあります:

http://www.sommarskog.se/arrays-in-sql.html

記事は長いですが、良い意味で。著者はSQLサーバーの専門家ですが、全体としての概念はMySQLにも当てはまります。

于 2011-04-15T19:50:04.323 に答える
1

私が知っているように、プリペアドステートメントのパラメータとして配列を提供することはできません。IN()は、配列としてのパラメーターをサポートしていません。

于 2011-04-15T19:20:26.673 に答える
0

そのように追加する方法はないと思いますが、リストを繰り返し処理してクエリを動的に生成できる可能性があります。

例えば:

var intArray = new List<int>(){1,2,3,4};
if (intArray.Count > 0) {
    var query = "SELECT * FROM table WHERE id IN (";
    for (int i = 0; i < intArray.Count; i++) {
        //Append the parameter to the query
        //Note: I'm not sure if mysql uses "@" but you can replace this if needed
        query += "@num" + i + ",";
        //Add the value to the parameters collection
        ...connection.Command.Parameters.AddWithValue("num" + i, intArray[i]);
    }
    //Remove the last comma and add the closing bracket
    query = query.Substring(0, query.Length - 1) + ");";
    //Execute the query here
}

このようにして、別のタイプのリストを使用しても、パラメーター化されたクエリのメリットを享受できます。ただし、リストが大きい場合にパフォーマンスの問題が発生するかどうかはわかりませんが、そうなると思います。

于 2011-04-15T19:30:50.843 に答える
0

これは巨大なリストではうまく機能しませんが、リストをパラメーターとして渡す必要ある場合に機能することがわかったのはこれだけです。

それ以外の

SELECT * FROM table WHERE id IN (@parameter)

あなたはこれをしなければなりません:

SELECT *
FROM table
WHERE INSTR(','+@parameter+',', ','+CAST(the_column AS CHAR) + ',')

次に、リストを次のように渡すことができますstring.Join(",", intArray)

応急修理ですが、機能します。

于 2011-04-16T09:17:29.793 に答える
0

Mudからの回答は、パラメーターリストの最初のintに対してのみ機能します。これは、たとえばidが1の場合、「2,1,3,4」は機能しないことを意味します。

FIND_IN_SET()とIN()を参照してください。

今のところコメントはできませんが、MattEllenからの回答も参照してください。彼の答えを編集しますが、できません。INSTRは、複数のIDを持つWHEREの場合には機能しないようです(結果に対してのみ返されます)。

しかし、に置き換えるINSTRLOCATE、彼のソリューションが機能するようになります(String.Join(",", intArray)パラメーターを追加して)...私からの賛成票:

LOCATE(CONCAT(',' , CAST(id AS CHAR) , ',') , CONCAT(',' , CAST(@paramter AS CHAR) , ',')) <> 0
于 2015-07-28T08:39:59.160 に答える
0

リチャードの答えに基づいて、SQLインジェクションを回避する同様のアプローチを思いつきました。

private MySqlCommand GetCommandWithIn(string sql, string sqlParam, ICollection<string> list)
{
    var command = new MySqlCommand(string.Empty, connection);
    var i = 0;
    var parameters = new List<string>(list.Count);

    foreach (var element in list)
    {
        var parameter = $"p{i++}";
        parameters.Add($"@{parameter}");
        command.Parameters.AddWithValue(parameter, element);
    }

    command.CommandText = sql.Replace(sqlParam, string.Join(',', parameters));

    return command;
}

そして、あなたはそれを次のように呼ぶことができます:

var types = new List<string> { "Type1", "Type2" };
var sql = "SELECT * FROM TABLE WHERE Type IN (@Type)"
using var command = GetCommandWithIn(sql, "@Type", types);

を避けるために、空のリストを確認することを忘れないでくださいIN ()

于 2020-10-16T14:40:45.947 に答える