4

显然,ExecuteReader 用于只读,而 ExecuteNonQuery 用于事务。但由于某种原因,即使我使用了 ExecuteReader,我仍然能够运行写入(插入、更新、删除)命令(在 textbox1 中输入)。我的代码有问题还是我误解了 ExecuteReader 的工作方式?

//MY CODE

string sqlStatement = textbox1.Text;

System.Data.SqlClient.SqlConnectionStringBuilder builder =
  new System.Data.SqlClient.SqlConnectionStringBuilder();
builder.DataSource = ActiveServer;
builder.IntegratedSecurity = true;

System.Data.SqlClient.SqlConnection Connection = new 
   System.Data.SqlClient.SqlConnection(builder.ConnectionString);
Connection.Open();

System.Data.SqlClient.SqlCommand command = new 
  System.Data.SqlClient.SqlCommand(sqlStatement, Connection);
System.Data.SqlClient.SqlDataReader reader = command.ExecuteReader();

dataGridView1.AutoGenerateColumns = true;

bindingSource1.DataSource = reader;
dataGridView1.DataSource = bindingSource1;

reader.Close();
Connection.Close();
4

3 回答 3

7

ExecuteReader只返回一个读取器,该读取器可以读取从 SQL 过程返回的行 - 它不会阻止您在提供该结果集的过程中运行任意 SQL。

执行插入/更新/删除然后立即返回结果集(因此从看起来像读取的代码)可能有点奇怪(读取:代码气味),应该检查它是否可以拆分为不同的操作。

于 2010-07-27T21:11:09.110 回答
5

虽然两者都执行 sql,但ExecuteReader预计会在记录ExecuteNonQuery 数受到影响的情况下返回记录。两者因此不同。但在内部,它们有多大不同取决于供应商的具体实施。您可以ExecuteReader单独使用所有数据库操作,因为它刚刚工作(直到现在),但由于它没有记录它并不是真正正确的方法。您可以更清楚地了解您的意图ExecuteNonQuery

就性能而言,我认为根本没有区别。我尝试了SQLite, MySqlClient, SqlClient,SqlServerCeVistaDb没有发现明显的差异。他们都应该在ExecuteReader内部以一种或另一种方式使用。

要点:

客户端:

private int InternalExecuteNonQuery(DbAsyncResult result, string methodName, bool sendToPipe)
{
    if (!this._activeConnection.IsContextConnection)
    {
        if (this.BatchRPCMode || CommandType.Text != this.CommandType || this.GetParameterCount(this._parameters) != 0)
        {
            Bid.Trace("<sc.SqlCommand.ExecuteNonQuery|INFO> %d#, Command executed as RPC.\n", this.ObjectID);
            SqlDataReader sqlDataReader = this.RunExecuteReader(CommandBehavior.Default, RunBehavior.UntilDone, false, methodName, result);
            if (sqlDataReader == null)
            {
                goto IL_E5;
            }
            sqlDataReader.Close();
            goto IL_E5;
        }
    IL_B5:
        this.RunExecuteNonQueryTds(methodName, flag);
    }
    else
    {
        this.RunExecuteNonQuerySmi(sendToPipe);
    }
IL_E5:
    return this._rowsAffected;
}

MySql客户端:

public override int ExecuteNonQuery()
{
  int records = -1;

  #if !CF
  // give our interceptors a shot at it first
  if ( connection != null && 
       connection.commandInterceptor != null &&
       connection.commandInterceptor.ExecuteNonQuery(CommandText, ref records))
    return records;
  #endif

  // ok, none of our interceptors handled this so we default
  using (MySqlDataReader reader = ExecuteReader())
  {
    reader.Close();
    return reader.RecordsAffected;
  }
}

如您所见MySqlClient,直接调用ExecuteReaderwhileSqlClient仅适用于某些条件。请注意,inserts 和updates 很少是瓶颈(通常是selects)。

正如我所说,您不会在 的帮助下获得受影响的行数ExecuteReader,因此请使用ExecuteNonQuery更好的方法来执行查询更直接的替换ExecuteReader方法是ExecuteScalar返回读取的第一行第一列中的数据。

要点:

客户端:

override public object ExecuteScalar()
{
    SqlConnection.ExecutePermission.Demand();

    // Reset _pendingCancel upon entry into any Execute - used to synchronize state
    // between entry into Execute* API and the thread obtaining the stateObject. 
    _pendingCancel = false;

    SqlStatistics statistics = null;
    IntPtr hscp;
    Bid.ScopeEnter(out hscp, "<sc.sqlcommand.executescalar|api> %d#", ObjectID);
    try
    {
        statistics = SqlStatistics.StartTimer(Statistics);
        SqlDataReader ds = RunExecuteReader(0, RunBehavior.ReturnImmediately, true, ADP.ExecuteScalar);

        object retResult = null;
        try
        {
            if (ds.Read())
            {
                if (ds.FieldCount > 0)
                {
                    retResult = ds.GetValue(0);
                }
            }
            return retResult;
        }
        finally
        {
            // clean off the wire 
            ds.Close();
        }
    }
    finally
    {
        SqlStatistics.StopTimer(statistics);
        Bid.ScopeLeave(ref hscp);
    }
}

MySql客户端:

public override object ExecuteScalar()
{
    lastInsertedId = -1;
    object val = null;

    #if !CF
    // give our interceptors a shot at it first
    if (connection != null &&
        connection.commandInterceptor.ExecuteScalar(CommandText, ref val))
        return val;
    #endif

    using (MySqlDataReader reader = ExecuteReader())
    {
        if (reader.Read())
            val = reader.GetValue(0);
    }

    return val;
}

因此,使用ExecuteReaderforExecuteScalar并没有任何性能差异。

于 2013-02-19T12:14:32.423 回答
3

这些方法的底层实现只是执行给定的 SQL 语句,因此您可以使用这些方法中的任何一种运行几乎任何语句。只是最终的结果是一个读取器试图返回一个预期的结果集,而另一个方法不期望一个结果集。使用 ExecuteReader 运行不产生结果集的语句可能效率稍低一些。提供者仍将创建一个数据读取器对象,这会增加一点成本(在大多数情况下可能可以忽略不计,除非提供者最终对服务器进行另一次往返调用)。

于 2010-07-27T21:11:53.320 回答