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?另一种解决方案是将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

您在这里有几个选项(按优先顺序):

  1. 使用支持表值参数的数据库。这是获得您想要的确切语法的唯一方法。
  2. 数据必须来自某个地方:您的数据库、用户操作或机器生成的源。

    • 如果数据已经在您的数据库中,请改用子查询。
    • 对于其他机器生成的数据,请使用 BULK INSERT、SqlBulkCopy 或您的数据库首选的批量导入工具。
    • 如果它是由用户创建的,请将其添加到每个单独的用户操作的单独表中,然后使用子查询。

      这方面的一个例子是购物车。用户可能会选择几个项目来购买。与其将这些保留在应用程序中,并且需要在结帐时一次性将所有项目添加到订单中,不如在用户选择或更改时将每个项目添加到数据库中的表中。

  3. 有一个 sql 用户定义的函数,它将字符串参数解压缩到表中,并将该表作为一个集合返回,您可以将其与 IN() 表达式一起使用。有关其工作原理的更多详细信息,请参阅下面的链接文章。
  4. 在客户端动态构建字符串列表或参数列表(如其他答案所示)。请注意,这是我最不喜欢的选项,因为它创建的代码往往容易受到 sql 注入问题的影响。

关于这个主题的最终(我的意思是明确的)工作在这里:

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

文章很长,但方式很好。作者是 sql server 专家,但总体概念也适用于 MySQL。

于 2011-04-15T19:50:04.323 回答
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 回答
1

据我所知,您不能提供任何数组作为准备好的语句的参数。IN() 不支持将参数作为数组。

于 2011-04-15T19:20:26.673 回答
0

这不适用于大型列表,但如果您必须将列表作为参数传递,这是我发现的唯一有效的方法。

代替

SELECT * FROM table WHERE id IN (@parameter)

你必须这样做:

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

然后你可以传入你的列表string.Join(",", intArray)

这是一个kludge,但它的工作原理。

于 2011-04-16T09:17:29.793 回答
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

来自 Mud 的回答仅适用于参数列表中的第一个 int。这意味着如果 id 为 1,则 '2,1,3,4' 将不起作用。

请参阅FIND_IN_SET() 与 IN()

目前无法发表评论,但也请参阅 Matt Ellen 的回答。会编辑他的答案,但不能。INSTR 似乎不适用于具有多个 id 的 WHERE 情况(仅返回结果)。

但是替换INSTRLOCATE使他的解决方案起作用(String.Join(",", intArray)添加了 as 参数)...来自我的 UP VOTE:

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

根据Richard的回答,我想出了一种类似的方法来避免 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 回答