SqlDataReader是面向连接的一个数据访问对象,是一个轻量级的、功能较弱的访问对象。区别于SqlDataAdapter和DataSet,SqlDataAdapter在从数据库中读取数据的时候,通过Fill方法把结果集填充到DataSet里,此时如果与数据库断开了,照样可以访问DataSet里的数据。对于SqlDataReader对象,因为它是面向连接的,每读取一条记录都必须实时的与数据库交互,读取一条,处理完后再到数据库里再取出一条记录来。若数据库断了,数据也就读不出来了。因为它是一条条的从数据库中读取结果集,而且是按照一定顺序读取的,读完了以后若要再次得到一样的结果集,必须重构SqlDataReader对象,然后在通过while语句来读取数据。(怎么使用while来读取数据及其其它相关知识这里不再赘述,可以参考msdn,http://msdn.microsoft.com/zh-cn/library/system.data.sqlclient.sqldatareader(v=vs.80).aspx#Y800)。
在过去的项目开发中,我们经常通过SqlDataReader对象的索引器来获取数据,如要得到列名为“SalesOrderDetailID”的记录,会使用dr[“SalesOrderDetailID”]来访问其值。当然我们也可以通过dr[0]来访问,其中0表示的是列的序号,如写了个查询语句:SELECT [SalesOrderDetailID],[ OrderQty] FROM [AdventureWorks].[Sales].[SalesOrderDetail]。其中dr[0]对应的是SalesOrderDetailID ,dr[1]对应的是OrderQty。还有第三种访问方式,通过GetValue方法访问,如dr.GetValue(0),其中0表示列的序号,同样还有Get+类型方法访问,如dr.GetInt32(0)。这时候问题出现了,四种访问方式一样吗,其效率如何?因此写了个小程序测试了下,先取样100,然后得出平均执行时间。数据库选择微软sqlserver里提供的示例数据库AdventureWorks,表使用SalesOrderDetail表,因为这张表中的记录条数>10万条,测试数据会比较准确。代码如下:
class Program
{
static void Main(string[] args)
{
string source=@"server=(local)/SQLSERVER2005;integrated security=SSPI;database=AdventureWorks";
float sum=0;
for (int i = 0; i < 100; i++)
{
try
{
using (SqlConnection conn = new SqlConnection(source))
{
conn.Open();
SqlCommand cmd = new SqlCommand("SELECT [SalesOrderDetailID] FROM [AdventureWorks].[Sales].[SalesOrderDetail]", conn);
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
sw.Start();
int o;
SqlDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
o = (int)dr["SalesOrderDetailID"];
//o = (int)dr[0];
//o = dr.GetInt32(0);
//o = (int)dr.GetValue(0);
}
dr.Close();
sw.Stop();
string tmp = string.Format("{0}.{1}", sw.Elapsed.Seconds, sw.Elapsed.Milliseconds);
sum += float.Parse(tmp);
Console.Write(tmp+"; ");
}
}
catch (SqlException ex)
{
Console.WriteLine(ex.Message);
Console.Read();
}
}
Console.WriteLine("平均时间是:"+sum/100);
Console.Read();
}
}
执行结果用如下:
| dr["SalesOrderDetailID"] | dr[0] | dr.GetValue(0) | dr.GetInt32(0) |
平均用时 | 0.6854 | 0.5416002 | 0.5030998 | 0.4504999 |
从上表可知访问效率:dr.GetInt32(0)>dr.GetValue(0) > dr[0]> dr["SalesOrderDetailID"]
以上是〉10万条记录的一个情况,如果记录数更多,4者的差别会更加的明显。这4者到底有什么样的区别呢?为什么会有性能差异呢?下面对其进行分析。
首先,针对于dr[0]、dr["SalesOrderDetailID"]两种方式。dr[0]是通过数字访问,0指的就是列在结果集中的序号,通过列序号就可以取得到值。而dr["SalesOrderDetailID"]中索引器使用的是文本,针对文本索引,其内部机制是通过文本索引到结果集的模式内部去查找列序号,然后再使用列序号进行访问。其过程比数字索引多了一个文本匹配的过程,这就是两者差异所在。至于dr.GetValue (0),我们看看msdn的描述:
为了获得最佳性能,SqlDataReader 会避免创建不必要的对象或复制不必要的数据。因此,对 GetValue 等方法的多次调用将返回对相同对象的引用。如果正在修改由 GetValue 等方法返回的对象的基础值,请使用警告。
故其性能是优于数字索引和文本索引的。对于dr.GetInt32(0)内部原理其实跟GeValue差不多,但是在程序中我们发现,前面三种访问方式再取到数据时,都必须进行拆箱操作,而装箱和拆箱操作对性能都有非常不利的影响,所以其优于其它3种方式。在表中我们也会发现,dr.GetInt32(0)> dr.GetValue(0)> dr[0]这三者用时比较相近,原因之一:dr[0]性能开销主要是在获取数据类型的时候,而dr.GetInt32则不用。
其实4种访问方式各有千秋。对于数据索引有点好处在于可以避免把数据库字段暴露给应用程序。而对于文本索引则相反,字段暴露给应用程序后,程序员就不用去记那些晦涩的数字,对于程序代码也是一目了然,对代码的开发和维护都所有帮助。到底是用哪种访问方式更好?只能根据实际项目的需要去权衡。