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

时间:2016-05-13 16:12:24

标签: 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.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;

4 个答案:

答案 0 :(得分:4)

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

存在ExecuteScalar更好。

随着更多信息支持:

根据http://sqlblog.com/blogs/andrew_kelly/archive/2007/12/15/exists-vs-count-the-battle-never-ends.aspx

  

两个查询都扫描了表格,但EXISTS至少能够这样做   部分扫描确实它可以在找到它之后停止   第一个匹配的行。 COUNT()必须读取每一行的位置   在整个表中确定它们是否符合标准和方式   很多都有。这是关键人物。停止工作的能力   在符合WHERE子句条件的第一行之后是   是什么让EXISTS如此高效。优化器知道此行为   也可以将其考虑在内。现在请记住这些表是   与现实世界中的大多数数据库相比相对较小。所以   COUNT()查询的数字将成倍增加   更大的桌子。你可以很容易地获得数以百计的读数或数百   更多关于具有数百万行的表,但仍然只有EXISTS   只需对可以使用索引来满足的任何查询进行一些读取   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)

另见

http://sqlmag.com/t-sql/exists-vs-count

UPDATE:在ExecuteScalar vs ExecuteReader上。 在System.Data.SqlClient.SqlCommand方法的实现上看一下反汇编程序(如Reflector),显示出一些令人惊讶的东西,它们是等价的:最终都调用了内部帮助器 内部SqlDataReader RunExecuteReader(CommandBehavior cmdBehavior,RunBehavior runBehavior,bool returnStream,字符串方法,TaskCompletionSource完成,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(来自https://dev.mysql.com/downloads/connector/net/https://github.com/mysql/mysql-connector-net)。

总结:关于ExecuteScalar vs ExecuteReader都会产生创建SqlDataReader的开销,我会说差异主要是惯用的。

答案 1 :(得分:3)

我与ExecuteScalar一起使用if exists之类的查询。它应该在服务器上尽可能快,并且网络流量最小。

答案 2 :(得分:1)

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

SELECT CASE WHEN EXISTS(SELECT ...) THEN 1 ELSE 0 END

答案 3 :(得分:1)

我会使用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);