C# 使用C检查SQL Server表中是否存在记录的最佳方法是什么?

C# 使用C检查SQL Server表中是否存在记录的最佳方法是什么?,c#,sql-server,tsql,C#,Sql Server,Tsql,最好的方法是什么?要拿回1或0?或者检查查询中的行是否可用?我支持ExecuteScalar,但对其他答案感兴趣,为什么 //using DataReader.HasRows? bool result = false; var cmd = new SqlCommand("select foo, bar from baz where id = 123", _sqlConnection, _sqlTransaction); cmd.CommandType = System.Data.Comman

最好的方法是什么?要拿回1或0?或者检查查询中的行是否可用?我支持ExecuteScalar,但对其他答案感兴趣,为什么

//using DataReader.HasRows?
bool result = false;

var cmd = new SqlCommand("select foo, bar from baz where id = 123", _sqlConnection, _sqlTransaction);

cmd.CommandType = System.Data.CommandType.Text;

using (var r = cmd.ExecuteReader())
{
    if (r != null && r.HasRows)
    {
        result = true;
    }
}

return result;

//or using Scalar?
bool result = false;

var cmd = new SqlCommand("if exists(select foo, bar from baz where id = 123) select 1 else select 0", _sqlConnection, _sqlTransaction);

cmd.CommandType = System.Data.CommandType.Text;

int i = (int) cmd.ExecuteScalar();
result = i == 1;
return result;

我会使用ExecuteScalar,使用类似于“如果存在”的查询。它应该在服务器上以尽可能快的速度运行,并且网络流量最小。

如果您只关心存在性,我将使用标量方法,但也会将TSQL更新为:

SELECT CASE WHEN EXISTS(SELECT ...) THEN 1 ELSE 0 END
我将使用ExecuteScalar进行稍微不同的查询:

string sql = "SELECT CASE WHEN exists(select  NULL from baz where id = 123) THEN 1 ELSE 0 END";
var cmd = new SqlCommand(sql, _sqlConnection, _sqlTransaction);

Exists比Count更有效,因为Count需要扫描所有行以匹配条件并包含在Count中,exist NOT

因此,ExecuteScalar更好

更多信息如下:

两个查询都扫描了表,但EXISTS至少能够扫描 部分扫描可以改变这样一个事实,即它可以在找到最关键的部分后停止 第一个匹配行。其中作为计数必须读取每一行 在整个表中确定它们是否符合标准以及如何匹配 有很多。这是关键人物。停止工作的能力 在满足WHERE子句标准的第一行之后 是什么使存在如此有效。优化器知道这种行为 也可以考虑到这一点。现在请记住,这些表是 与现实世界中的大多数数据库相比,相对较小。所以 计数查询的数字将在计算机上成倍增加 更大的桌子。你可以很容易地得到成百上千的阅读或阅读 更多关于具有数百万行的表的信息,但仍然只存在 对任何可以使用索引来满足的查询只进行几次读取 WHERE子句

作为一个简单的实验,使用AdventureWorks和MSSQL 2012

set showplan_all on

-- TotalSubtreeCost: 0.06216168
select count(*) from sales.Customer

-- TotalSubtreeCost: 0.003288537
select 1 where exists (select * from sales.Customer)
另见

更新:关于ExecuteScalar vs ExecuteReader。 在System.Data.SqlClient.SqlCommand方法的实现上使用类似于反汇编程序的反射器进行查看,会发现一些令人惊讶的事情,它们在某种程度上是等价的:两者最终都调用了内部助手 内部SqlDataReader RunExecuteReaderCommandBehavior cmdBehavior,RunBehavior RunBehavior,bool returnStream,string方法,TaskCompletionSource completion,int超时,out任务,bool asyncWrite=false

它返回一个SqlDataReader,ExecuteReader按原样返回它。 当ExecuteScalar使用另一个助手时:

private object CompleteExecuteScalar(SqlDataReader ds, bool returnSqlValue)
{
    object obj2 = null;
    try
    {
        if (!ds.Read() || (ds.FieldCount <= 0))
        {
            return obj2;
        }
        if (returnSqlValue)
        {
            return ds.GetSqlValue(0);
        }
        obj2 = ds.GetValue(0);
    }
    finally
    {
        ds.Close();
    }
    return obj2;
}
另一方面,MySQL Connector/NET(MySQL的官方ADO.NET开源驱动程序)也是如此,ExecuteScalar方法在内部创建了一个更精确的DataReader MySqlDataReader并使用它。请参阅或中的源文件/Src/Command.cs


小结:关于ExecuteScalar和ExecuteReader,两者都会导致创建SqlDataReader的开销,我想说的是,两者的区别主要是惯用的。

您想要查询结果,还是仅仅是一个指标,如果有结果的话?第三个选项是使用select top 1 from。。。这听起来至少是最简单的。如果您对数据库端发生的事情感兴趣,那么无论如何都应该查看执行计划和统计io输出。计数显然是最无效的。关于ExecuteScalar vs ExecuteReader,你能说些什么?@AlexKudryashev我更新了答案,添加了一些细节。我怀疑DataReader参与了每一个选择的操作。在这种情况下,ExecuteReader更有效。越少越好。