返回标识值时的 ExecuteScalar 与 ExecuteNonQuery

Posted

技术标签:

【中文标题】返回标识值时的 ExecuteScalar 与 ExecuteNonQuery【英文标题】:ExecuteScalar vs ExecuteNonQuery when returning an identity value 【发布时间】: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;
        
    

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

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

我正在使用 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 的目的。 ***.com/a/9319609/284240 要记住的重要一点是应用程序的结构、目标和最简单的方法。正如 Aaron 所说,您可以采用存储过程路线;您甚至可以利用大型查询来提取数据并在代码中编译它们。这是一个偏好问题。但 Sam 对于“一般”目的是正确的。 @TimSchmelter 做到了。我永远不会猜到整个numeric 的事情,尤其是因为我的身份列是int(并且它在输出参数上没有强制转换)。我什至去找了reason why。 【参考方案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#。

以上是关于返回标识值时的 ExecuteScalar 与 ExecuteNonQuery的主要内容,如果未能解决你的问题,请参考以下文章

当磁盘使用率高时,使用 SqlCommand.ExecuteScalar() 从序列中选择返回 NULL

JsonResult作为Action返回值时的错误

为啥插入成功,ExecuteScalar却返回0

OracleCommand ExecuteScalar 有时返回 null

更改linux 切换用户时的标识符和颜色

为啥 OleDb ExecuteScalar 方法在查询 COUNT 时返回 Decimal?