【问题标题】:C# SQL parameter query with in [duplicate]C# SQL 参数查询与 [重复]
【发布时间】:2011-12-11 20:11:31
【问题描述】:

可能重复:
Parameterizing a SQL IN clause?

我有以下代码:

var myCommand = new SqlCommand();
myCommand.Parameters.Add("@username", SqlDbType.NVarChar);
myCommand.Parameters["@username"].Value = username;
return _dbConnection.ExecuteQuery("Select * from customer where username = @username");

现在我需要调整查询以获得更多值。我想做这样的事情:

var myCommand = new SqlCommand();
foreach (string username in usernames){
  myCommand.Parameters.Add("@username", SqlDbType.NVarChar);
  myCommand.Parameters["@username"].Value = username;
}
return _dbConnection.ExecuteQuery("Select * from customer where username in @username");

这可能吗?怎么样?

谢谢!

BR斯特凡

【问题讨论】:

    标签: c# sql-server parameters


    【解决方案1】:

    我喜欢用两种方法来做这样的事情。

    public void ReadDatabase(string[] values)
    {
        foreach (var value in values)
        {
            using (var rd = GetData(value))
            {
                if (!rd.Read())
                    throw new OMGException("FAILED!");
    
                Console.WriteLine(rd["UserId"].ToString());
            }
        }
    }
    
    public IDataReader GetData(string value)
    {
        using(var cm = _connectionWhatever.CreateCommand())
        {
            cm.CommandText = @"
                Select UserId
                From MyTable
                Where UserName = @User
            ";
            cm.CommandType = CommandType.Text;
            cm.Parameters.AddWithValue("User", value);
            return cm.ExecuteReader();
        }
    }
    

    这只是给你一些想法。希望这会有所帮助。

    【讨论】:

    • OMGException 的虚拟 +1!
    【解决方案2】:

    把代码改成这个

        var myCommand = new SqlCommand();
        int i = 0;
        string param = string.Empty;
    
        foreach (string username in usernames)
        {
            string paramName = string.Format("@username{0}", i);
            myCommand.Parameters.Add(paramName, SqlDbType.NVarChar);
            myCommand.Parameters[paramName].Value = username;
            param += string.Format("  (username={0}) or", paramName);
    
            i++;
        }
        param = param.Substring(0, param.Length - 2);
        return _dbConnection.ExecuteQuery("Select * from customer where " + param);
    

    【讨论】:

    • 同时使用 or 代替 IN 更快
    【解决方案3】:

    好吧,如果你真的愿意,你可以完全通过字符串操作来做到这一点。

    string sql = "Select * from customer where username in (";
    
    string comma = "";
    foreach(string s in usernames)
    {
       sql += comma + "'" + CleanSqlParameter(s) + "'";
       comma = ", ";
    }
    
    return _dbConnection.ExecuteQuery(sql);
    

    这会很混乱!但是,如果您觉得您需要完全在 C# 中构建查询,这里有一种方法可以做到。我用这种方法取得了成功。

    【讨论】:

    • 请务必确保usernames 对象不是从页面上的文本框构建的,否则这将容易发生 SQL 注入
    • @Curt - 绝对正确。实际上,我在清理部分有一个过程,可确保查询中没有不安全的词,例如“N、DELETE、UPDATE、INSERT”。这只是一个示例。
    • 是的,只要用户无法操作用户名对象中的值,我认为这是一个可以接受的解决方案
    • @Curt - 比这更糟糕。数据库中的所有内容都是由人类在某个时候输入到计算机中的。它们在第一次放入时可能是安全的,但是以这种方式循环它们时,您现在就有了所谓的 2nd Order Sql Injection 漏洞。这只是构建查询的一种非常糟糕的方式,无论数据来自何处。
    • @MAW74656 - 清理过程是良好参数化的弱且容易失败的替代品。还有更好的方法。
    【解决方案4】:

    每个参数都必须是唯一的:

    var paramNames = new List<string>();
    var myCommand = new SqlCommand();
    foreach (string username in usernames){
      var paramName = "@user" + paramNames.Count;
      myCommand.Parameters.Add(paramName, SqlDbType.NVarChar);
      myCommand.Parameters[paramName].Value = username;
      paramNames.Add(paramName);
    }
    return _dbConnection.ExecuteQuery("Select * from customer where username in (" + string.Join(",", paramNames) + ")");
    

    你会得到这样的sql:

    SELECT * from customer where username in (@user0, @user1, @user2)
    

    从查询计划缓存的角度来看,它没有多大帮助,但您将获得不易受到 sql 注入攻击的好处。

    【讨论】:

      【解决方案5】:

      您不能完全按照您对循环的想法进行操作,因为您将多次使用不同的值添加相同的参数,但这可能会有所帮助:

      http://support.microsoft.com/kb/555266(在 VB 中,但您可能可以找到 c# 示例或翻译)

      我自己解决这个问题的方法是把它变成一个存储过程作为它的好习惯(好处,比如增加安全性、效率、代码重用等),然后让你的过程接受 XML 的参数。在您的 C# 应用程序中,将 Usernames 集合转换为 XML,然后将其传递给 SP。

      【讨论】:

        【解决方案6】:

        这可以通过将 XML 数据类型传递给 Query 并在其中进行搜索来完成。

        declare @x xml
        set @x = '<IDs><ID>1</ID><ID>2</ID></IDs>'
        
        SELECT ID.value('.', 'int') AS ID
        FROM @x.nodes('/IDs/ID') as IDS(ID)
        

        所以在这种情况下你可以这样做:

        var myCommand = new SqlCommand();
        myCommand.Parameters.Add("@usernames", SqlDbType.Xml);
        string usernames = "<Usernames>"
        foreach (string username in usernames){
           usernames+= String.Format("<username>{0}</username>", username);
        }
        usernames += "</Usernames>"
        myCommand.Parameters["@usernames"].Value = usernames;
        return _dbConnection.ExecuteQuery("Select * from customer where username in (SELECT
            username.value('.', 'nvarchar') AS Username
            FROM @x.nodes('/Usernames/Username') as Usernames(Username))");
        

        但是,语法可能需要检查

        【讨论】:

        • 不完全是性能最高的选项,但这个概念应该有效。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-02-22
        相关资源
        最近更新 更多