【问题标题】:Add multiple SQL values with same parameterized query?使用相同的参数化查询添加多个 SQL 值?
【发布时间】:2016-10-28 18:13:48
【问题描述】:

我对 SQL 还很陌生,并试图找出添加一些预定义数据的最佳方法。我在这里搜索发现我应该使用参数化命令来避免 sql 注入攻击,在这种情况下这不是一个大问题,但我想避免这种可能性并学会正确地做......无论如何这里是我现在的代码:

        using (SqlTransaction trans = connection.BeginTransaction())
        {
            foreach (IEnumerable<string> row in table.RowData)
            {
                using (SqlCommand sql = new SqlCommand("INSERT INTO " + table.Title
                    + " (" + string.Join(", ", table.Headers)
                    + ") VALUES (" + string.Join(", ", table.Headers.Select(x => "@" + x)) + ");", connection, trans))
                {

                    for (int i = 0; i < table.Headers.Count(); i++)
                    {
                        if (string.IsNullOrEmpty(row.ElementAt(i)))
                        { sql.Parameters.AddWithValue("@" + table.Headers.ElementAt(i), DBNull.Value); }
                        else
                        { sql.Parameters.AddWithValue("@" + table.Headers.ElementAt(i), row.ElementAt(i)); }
                    }
                    sql.ExecuteNonQuery();
                }
            }
            trans.Commit();
        }

这似乎有效,所有数据都进入了那里,但对我来说“感觉”效率低下。我将它包装在一个事务中,所以只有一个提交,但它每次都创建参数,只是为每一行设置不同的值。

有没有办法让它使用相同的参数但只为每行设置不同的值?还是这是最好的方法,我不应该担心?

提前感谢您提供的任何帮助。

【问题讨论】:

  • c# 中的对象很便宜。能不能更有效率?可能。但是你不会通过重用一个参数来获得很多东西,你甚至可能会通过这样做引入一些微妙的错误。我建议不要这样做。
  • 有了更多的上下文,我们可以提供更好的帮助,因为我们不知道这是否应该是 SQL API 的可重用包装器、具有大量插入语句和值的特定查询,还是什么- 答案取决于。
  • 很抱歉不得不告诉您这一点,但这很容易受到注入攻击——您将变量字符串 (table.Title) 连接到查询中。
  • 另外,如果您没有执行回滚的代码,我认为包装事务没有任何意义。如果您这样做,则更有可能出现死锁。
  • 不,您不能对表名和列名使用参数,只能使用值。

标签: c# sql .net sql-server database


【解决方案1】:

我们可以通过在预处理步骤中将标头解析为参数来做您想做的事情。我还删除了显式事务,因为默认情况下每个插入都已经获得了一个隐式事务(为什么要支付两个事务的性能损失?)。

using (var command = new SqlCommand()) {
    command.CommandText =
        "INSERT INTO " + table.Title + " ("
      + string.Join(", ", table.Headers)
      + ") VALUES ("
      + string.Join(", ", table.Headers.Select(x => "@" + x))
      + ");";
    command.Connection = connection;

    foreach (var header in table.Headers) {
        /*
             Add all parameters as strings. One could choose to infer the
             data types by inspecting the first N rows or by using some sort
             of specification to map the types from A to B.
         */
        command.Parameters.Add("@" + header, typeof(string));
    }

    foreach (var row in table.RowData) {
        for (var i = 0; i < table.Headers.Count(); i++) {
            if (!string.IsNullOrEmpty(row.ElementAt(i))) {
                command.Parameters["@" + table.Headers.ElementAt(i)].Value = row.ElementAt(i);
            }
            else {
                command.Parameters["@" + table.Headers.ElementAt(i)].Value = DBNull.Value;
            }
        }

        command.ExecuteNonQuery();
    }
}

【讨论】:

  • 好的,我明白了,除了 command.Parameters.Add() 需要数据类型的第二个参数...不太难做,但 AddWithValue 似乎自动识别数据类型很好...
  • @sfaust 确实如此,基于传入的值。由于我们正在提升逻辑,因此您必须提前确定类型。一种方法是使用第一行 n 推断数据类型。
  • 是的,做起来并不难,只是想主要为提出这个问题的其他人指出这一点。我会稍作修改以获取数据类型,但谢谢,这正是我想要的!
  • 另外,它应该是 command.Parameters[...].Value = ... 你在那里没有价值。我可以要求您更新答案和数据类型,以免其他新手感到困惑吗? :)
  • @sfaust 当然。抱歉,我没有测试就写了这个。
【解决方案2】:

这是适合我的插入示例

  private void insertWordCount(string songId, string wordId, int wordCount)
    {
        string query = "insert into songs_words_conn values(@wordId,@songId,@wordCount)";
        SqlCommand cmd = new SqlCommand(query, conn);

        cmd.Parameters.AddWithValue("@wordId", wordId);
        cmd.Parameters.AddWithValue("@songId", songId);
        cmd.Parameters.AddWithValue("@wordCount", wordCount);

        cmd.ExecuteNonQuery();
    }

【讨论】:

  • 这确实有效,基本上就是我正在做的事情。但是,为了向表中添加多个值,它必须循​​环多次创建参数,这是我试图避免的......
【解决方案3】:

是的,通过重用 SqlParameter 对象可以提高效率。这是一些伪代码:

        const string sql = "INSERT INTO table1 (column1) VALUES (@p0)";
        using (var sqlCommand = new SqlCommand(sql, connection, transaction))
        {
            var param1 = sqlCommand.Parameters.Add("@p0", SqlDbType.Int);
            foreach (var row in table)
            {
                param1.Value = row["value"];
                sqlCommand.ExecuteNonQuery();
            }
        }

【讨论】:

  • 这是在做一些不同的事情。这只是每行添加一列。这究竟是一个怎样的答案?
  • 这向您展示了 如何 在 foreach 中重用 SQL 参数,这是问题的要点。使用一点批判性思维将使用户得到完整的答案。
  • 一点也不可怕,@jhilden 正在 foreach 循环之外构造参数,而我在循环内构造它。原始代码每次都在创建新代码,而此代码不是。有多可怕?
猜你喜欢
  • 1970-01-01
  • 2022-06-25
  • 2019-02-12
  • 2018-08-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-20
  • 2017-02-15
相关资源
最近更新 更多