【问题标题】:ExecuteScalar vs ExecuteNonQuery when returning an identity value返回标识值时的 ExecuteScalar 与 ExecuteNonQuery
【发布时间】:2012-12-24 04:20:02
【问题描述】:

如果我想返回新插入的行的标识列,则尝试确定最好使用ExecuteScalarExecuteNonQuery。我已经阅读了this question 并且我理解那里的差异,但是在查看我几周前编写的一些代码时(同时大量从这个站点借用)我发现在我的插入中我使用了ExecuteScalar,就像这样:

public static int SaveTest(Test newTest)
{
    var conn = DbConnect.Connection();
    const string sqlString = "INSERT INTO dbo.Tests ( Tester , Premise ) " +
                             "               VALUES ( @tester , @premise ) " +
                             "SET @newId = SCOPE_IDENTITY(); ";
    using (conn)
    {
        using (var cmd = new SqlCommand(sqlString, conn))
        {
            cmd.Parameters.AddWithValue("@tester", newTest.tester);
            cmd.Parameters.AddWithValue("@premise", newTest.premise);
            cmd.Parameters.Add("@newId", SqlDbType.Int).Direction = ParameterDirection.Output;

            cmd.CommandType = CommandType.Text;
            conn.Open();
            cmd.ExecuteScalar();

            return (int) cmd.Parameters["@newId"].Value;
        }
    }
}

这可以满足我的需要,所以我想知道

  1. 我是否应该在这里使用ExecuteNonQuery,因为它“更适合”进行插入?
  2. 由于我使用的是输出参数,因此检索标识值是否相同?
  3. 是否存在与一种或另一种方式相关的性能影响?
  4. 总体上是否有更好的方法来做到这一点?

我正在使用 Visual Studio 2010、.NET 4.0 和 SQL Server 2008r2,以防万一。

【问题讨论】:

  • (1) 为什么ExecuteNonQuery“更合适”? (2) 你考虑过使用存储过程吗?如果不是,为什么不呢?它肯定会帮助清理您放入应用程序的所有临时 SQL - 当您必须更改它时,这意味着您必须重新编译和重新部署应用程序。
  • 嗯... ExecuteNonQuery 通常用于执行不期望返回结果的 SQL。 ExecuteScalar 返回一个值,因此您不需要传递参数。您可以将 SQL 的最后一部分更改为 SELECT SCOPE_IDENTITY(); 然后使用 return (int)cmd.ExecuteScalar();
  • 我使用ExecuteScalar 因为我使用SELECT SCOPE_IDENTITY 没有输出参数,因此检索单个值这是ExecuteScalar 的目的。 stackoverflow.com/a/9319609/284240
  • 要记住的重要一点是应用程序的结构、目标和最简单的方法。正如 Aaron 所说,您可以采用存储过程路线;您甚至可以利用大型查询来提取数据并在代码中编译它们。这是一个偏好问题。但 Sam 对于“一般”目的是正确的。
  • @TimSchmelter 做到了。我永远不会猜到整个numeric 的事情,尤其是因为我的身份列是int(并且它在输出参数上没有强制转换)。我什至去找了reason why

标签: c# sql-server executenonquery executescalar


【解决方案1】:

正如 Aaron 所建议的,存储过程会使其更快,因为它可以节省 Sql Server 编译 SQL 批处理的工作。但是,您仍然可以采用任何一种方法:ExecuteScalarExecuteNonQuery。恕我直言,它们之间的性能差异是如此之小,以至于任何一种方法都是“正确的”。

话虽如此,如果您从输出参数中获取标识值,我看不出使用 ExecuteScalar 的意义。在这种情况下,ExecuteScalar 返回的值将变得毫无用处。

我喜欢的一种方法,因为它需要更少的代码,使用 ExecuteScalar 没有输出参数:

public static int SaveTest(Test newTest)
{
    var conn = DbConnect.Connection();
    const string sqlString = "INSERT INTO dbo.Tests ( Tester , Premise ) " +
                             "               VALUES ( @tester , @premise ) " +
                             "SELECT SCOPE_IDENTITY()";
    using (conn)
    {
        using (var cmd = new SqlCommand(sqlString, conn))
        {
            cmd.Parameters.AddWithValue("@tester", newTest.tester);
            cmd.Parameters.AddWithValue("@premise", newTest.premise);

            cmd.CommandType = CommandType.Text;
            conn.Open();
            return (int) (decimal) cmd.ExecuteScalar();

        }
    }
}

编程愉快!

编辑:注意我们需要转换两次:从对象到decimal,然后到int(感谢techturtle 注意到这一点)。

【讨论】:

  • 我也遇到了选角问题。但有时不需要,不知道为什么?
  • 我也使用这种方法(ExecuteScalar 与 SCOPE_IDENTITY 附加到 SQL 命令。唯一的“陷阱”是如果您采用这种方法和通用插入函数并将其传递给(喘气)不'没有身份驱动的主键。如果是这样,您将在 VB.NET 中收到错误 Conversion from type 'DBNull' to type 'String' is not valid(问我是如何知道的),我假设您也会收到与该错误等效的 c#。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-08-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-19
  • 2016-12-23
  • 1970-01-01
相关资源
最近更新 更多