执行返回零行的SELECT SQL语句的ExecuteNonQuery方法

6

当使用ExecuteNonQuery执行SELECT SQL语句时,如何检查是否返回了零行?


2
请提供一些背景信息,最好附带代码。你的 command 有参数吗? - AJ.
4个回答

34

ExecuteNonQuery方法返回由INSERTUPDATEDELETE语句影响的行数。此方法用于执行DML(数据操作语言)语句,正如前面所述。

ExecuteReader方法将返回SELECT的结果集。当您查询一堆结果(例如来自表、视图等的行)时,请使用此方法。

ExecuteScalar方法将从SELECT语句中的第一行第一列返回单个值。当您只期望从查询中返回一个值时,请使用此方法。

简而言之,在使用ExecuteNonQuery方法时,没有结果来自SELECT语句是正常的。请改用ExecuteReader。使用ExecuteReader方法,您将通过返回的SqlDataReader对象的实例知道返回了多少行。

int rows = 0;

if (reader.HasRows)
    while (reader.Read())
        rows++;

return rows; // Returns the number of rows read from the reader.

@JLILIAmen,这个属性在2010年就有了吗?这是我第一次听说它。如果现在有了这个属性,那太好了。感谢您的评论。 - Will Marcouiller

6

我看不到任何方法来实现这一点。使用ExecuteScalarselect count(*) where来计算与原始SELECT查询的条件匹配的行数。下面是一个示例,摘自此处:

using (SqlCommand thisCommand = 
    new SqlCommand("SELECT COUNT(*) FROM Employee", thisConnection))
{
    Console.WriteLine("Number of Employees is: {0}",
       thisCommand.ExecuteScalar());
}

如果您需要行,那么我想您已经在使用ExecuteReader了。

我无法更改SQL查询,因为它是动态生成的。 - Lyle

3

请使用ExecuteReader方法代替。这将返回一个SqlDataReader,该对象包含一个HasRows属性。

ExecuteNonQuery不应用于SELECT语句。


1
这可能有些晚了,但我最近遇到了这个问题,认为对其他人(比如我)在以后寻求相同问题的帮助时会有所帮助。 无论如何,我相信您实际上可以按照您尝试的方式使用ExecuteNonQuery。 但是...您必须将底层SELECT查询调整为一个存储过程,该存储过程具有SELECT查询和输出参数,该参数设置为等于行计数。
如MSDN文档中所述:
尽管ExecuteNonQuery不返回任何行,但映射到参数的任何输出参数或返回值都将填充数据。
鉴于此,以下是我的做法。 顺便说一句,如果有任何缺陷,请向专家们提供反馈,但它似乎对我有效。
首先,您的存储过程应该有两个SELECT语句:一个用于返回数据集,另一个绑定到输出参数以返回记录计数:
CREATE PROCEDURE spMyStoredProcedure
(
     @TotalRows int output
)
AS
BEGIN
     SELECT * FROM MyTable; //see extra note about this line below.
     SELECT @TotalRows COUNT(*) FROM MyTable;
END

第二步,添加此代码(使用SqlCommand等vb.net).
Dim cn As SqlConnection, cm As SqlCommand, dr As SqlDataReader
Dim myCount As Int32

cn = New SqlConnection("MyConnectionString")
cn.Open() //I open my connection beforehand, but a lot of people open it right before executing the queries. Not sure if it matters.

cm = New SqlCommand("spMyStoredProcedure", cn)
cm.CommandType = CommandType.StoredProcedure
cm.Parameters.Add("@TotalRows", SqlDbType.Int).Direction = ParameterDirection.Output
cm.ExecuteNonQuery()

myCount = CType(cm.Parameters("@TotalRows").Value, Integer)
If myCount > 0 Then
     //Do something.
End If

dr = cm.ExecuteReader()
If dr.HasRows Then
     //Return the actual query results using the stored procedure's 1st SELECT statement
End If
dr.Close()
cn.Close()
dr = Nothing
cm = Nothing
cn = Nothing

就是这样。

另外一点说明,我假设你可能希望获取"MyCount"的数量来执行其他操作,而不仅仅是确定是否继续返回查询结果。原因是因为使用这种方法,你实际上不需要这样做。由于在获取计数后我使用了"ExecuteReader"方法,我可以利用数据读取器的"HasRows"属性来确定是否继续返回预期的数据集。但是,要返回一个数据集,你需要一个返回数据集的SELECT语句,这就是我在存储过程中使用第一个SELECT语句的原因。

顺便说一下,使用"ExecuteNonQuery"方法的好处是,在关闭DataReader之前,你可以使用它来获取总行数(你不能在关闭DataReader之前读取输出参数,这就是我试图做的事情,这种方法可以解决这个问题)。我不确定这样做是否会影响性能或存在缺陷,但像我说的那样...对我来说有效。=D


是的,由于在“SqlCommand”中执行SQL两次,会有性能损失。如果有DML语句,它们将被执行两次。此外,没有理由在执行“SqlCommand”之前打开连接;这只意味着您保持连接打开的时间更长。您可以将其移动到参数声明之后。Connection.Open()直到最后的“End If”应该在Try / Catch中,以便如果发生异常,Reader和Connection可以在Finally块中正确关闭。 - Solomon Rutzky
感谢您对Connection.Open放置位置的建议。很有道理。至于性能损失,我知道运行SqlCommand 2次会有一定的“损失”。但是,第一次执行不返回行,所以第一次执行是否最小?我假设第一次很快,因为它没有拉取整个数据集。因此,即使有100万条基础记录,第一个SqlCommand的性能损失也很小。如果我理解有误,请纠正我。我还没有测试过,只是根据定义假设它是这样工作的。 - ptownbro
不拉回结果并不意味着SQL Server不需要找到它们。如果您提交一个查询,SQL Server必须执行以下步骤:1)解析它,2)编译执行计划,3)执行它,并可选地4)返回结果。即使您丢弃结果集指针,前三个步骤仍会发生。是的,您可以节省传输数据所需的时间,这是一些节约,但不多。鉴于执行计划和数据页面上的缓存,第一次运行通常是较长的。简单查询(无排序/分组)可能还好,但在大多数情况下,这是一种昂贵的方法。 - Solomon Rutzky

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接