返回标识值时的 ExecuteScalar 与 ExecuteNonQuery
Posted
技术标签:
【中文标题】返回标识值时的 ExecuteScalar 与 ExecuteNonQuery【英文标题】:ExecuteScalar vs ExecuteNonQuery when returning an identity value 【发布时间】:2012-12-24 04:20:02 【问题描述】:如果我想返回新插入的行的标识列,则尝试确定最好使用ExecuteScalar
或ExecuteNonQuery
。我已经阅读了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 批处理的工作。但是,您仍然可以采用任何一种方法:ExecuteScalar
或 ExecuteNonQuery
。恕我直言,它们之间的性能差异是如此之小,以至于任何一种方法都是“正确的”。
话虽如此,如果您从输出参数中获取标识值,我看不出使用 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