首页 > 代码库 > 深入详解DataTable

深入详解DataTable

在学习DataTable知识之前,我们有必要了解下ADO.NET。以下摘自MSDN:

   ADO.NET 对 Microsoft SQL Server 和 XML 等数据源以及通过 OLE DB 和 XML 公开的数据源提供一致的访问。数据共享使用者应用程序可以使用 ADO.NET 来连接到这些数据源,并检索、处理和更新所包含的数据。
ADO.NET 通过数据处理将数据访问分解为多个可以单独使用或一前一后使用的不连续组件。ADO.NET 包含用于连接到数据库、执行命令和检索结果的 .NET Framework 数据提供程序。您可以直接处理检索到的结果,或将其放入 ADO.NET DataSet 对象,以便与来自多个源的数据或在层之间进行远程处理的数据组合在一起,以特殊方式向用户公开。ADO.NET DataSet 对象也可以独立于 .NET Framework 数据提供程序使用,以管理应用程序本地的数据或源自 XML 的数据。
ADO.NET 类在 System.Data.dll 中,并且与 System.Xml.dll 中的 XML 类集成。当编译使用 System.Data 命名空间的代码时,请引用 System.Data.dll 和 System.Xml.dll。有关连接到数据库、从数据库中检索数据并在命令提示中显示该数据的 ADO.NET 应用程序示例,请参见 ADO.NET 示例应用程序。
ADO.NET 向编写托管代码的开发人员提供了类似于 ActiveX 数据对象 (ADO) 为本机组件对象模块 (COM) 开发人员提供的功能。

ADO.NET中包含的对象及其关系如下图:

技术分享

技术分享

 

1、DataTable简介

 

1.1 DataTable的定义

 

  表示内存中数据的一个表。 我们知道数据库中存储的是实体表,实体表中有一系列的数据。而DataTable即存储在内存中的表,在持久化到数据库之前,是不会对数据库产生影响的,持久化到数据库可以使用dataAdapter.Update的方法(dataAdapter是某个实例化的DataAdapter对象)。

注意:当访问 DataTable 对象时,请注意它们是按条件区分大小写的。例如,如果一个 DataTable 被命名为“mydatatable”,另一个被命名为“Mydatatable”,则用于搜索其中一个表的字符串被认为是区分大小写的。但是,如果“mydatatable”存在而“Mydatatable”不存在,则认为该搜索字符串不区分大小写。


1.2 得到DataTable

 

  得到DataTable有许多方法,下面简单罗列出来:

1.2.1通过构造函数得到DataTable 
DataTable() 不带参数初始化DataTable 类的新实例。 
DataTable(string tableName) 用指定的表名初始化DataTable 类的新实例。 
DataTable(string tableName, string tableNamespace) 用指定的表名和命名空间初始化DataTable 类的新实例。

1.2.2通过DataSet获取DataTable

DataTable dt=ds.Tables["TableName"];//TableName是表名

1.2.3 通过DataRow自定义DataTable的结构

DataTable dt= new DataTable("TB_USER");
DataColumn colUserID = new DataColumn("USER_ID", Type.GetType("System.Int"));
dt.Columns.Add(colCurrency);
DataColumn colUserName= new DataColumn("USER_NAME", Type.GetType("System.String"));
dt.Columns.Add(colUserName);
这样得到是一个表的结构,里面没有任何数据,表面为TB_USER。
1.2.4通过已有的DataTable得到新的DataTable
可以使用DataTable.Clone()方法获得现有DataTable的表的结构,这在实际中也是常用的
1.2.5通过DataAdapter填充DataTable
DataAdapter.Fill(dt);来填充DataTable,这也是新手常用的方法,通常是些sql语句,然后使用command,是最基础的方法。
1.2.6通过DataRow数组导入DataTable
DataRow [] drs;//drs是某个有数据的DataRow数组

foreach(DataRow dr in drs)

{

dt.ImportRow(dr);

}

 

代码 

Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->   DataTable dt = new DataTable();
            
            dt.Columns.Add("id", typeof(int));

            dt.Rows.Add(new Object[] { 1 });

            DataTable dt2 = new DataTable();

            dt2=dt.Clone();

            foreach (DataRow dr in dt.Rows)
            {
                dt2.ImportRow(dr);
            }

            foreach (DataRow item in dt2.Rows)
            {
                Response.Write(item["id"]);
            }

1.3  DataTable常用属性 


CaseSensitive 指示表中的字符串比较是否区分大小写。

ChildRelations 获取此DataTable 的子关系的集合。

Columns 获取属于该表的列的集合。

Constraints 获取由该表维护的约束的集合。

DefaultView 获取可能包括筛选视图或游标位置的表的自定义视图。

HasErrors 获取一个值,该值指示该表所属的

DataSet 的任何表的任何行中是否有错误。

MinimumCapacity 获取或设置该表最初的起始大小。该表中行的最初起始大小。默认值为 50。

Rows 获取属于该表的行的集合。

TableName 获取或设置DataTable 的名称。

 

1.4 DataTable是ADO.NET中的重要成员

 

  DataSet中可包括多个 DataTable,可将多个查询结构存到一个DataSet中,方便操作,而DataTable中又包括多个DataRow、DataColumn,可通过这些DataRow、DataColumn来查看、操作其中的数据,而需将操作结果返回给数据库的话,则可以调用DataAdapter的 Update方法。

 

 

 

2、DataTable成员之DataRow

 

 

  DataTable是由一个个DataRow组合而成,DataTable.Rows[i]即表示其中的第i行。

  DataRow有一个十分重要的状态(RowState),这个状态经常被我们忽略,从而导致一些莫名其妙的bug。RowState 的值是一个枚举类型的,RowState 有 Added, Modified, Unchanged, Deleted, Detached 几种, 分别表示 DataRow 被添加, 修改, 无变化, 删除, 从表中脱离. 在调用一些方法或者进行某些操作之后, 这些状态可以相互转化。我们不做什么判断就开始操作DataRow,这就有可能导致某些状态为Deleted的行也同时被操作,这样就有可能导致脏数据的产生。

 

 

RowState 值

说明

Unchanged

自上次调用 AcceptChanges 之后,或自 DataAdapter.Fill 创建了行之后,未做出过任何更改。

Added

已将行添加到表中,但尚未调用 AcceptChanges。

Modified

已更改了行的某个元素。

Deleted

已将该行从表中删除,并且尚未调用 AcceptChanges。

Detached

该行不属于任何 DataRowCollection。新建行的 RowState 设置为 Detached。通过调用 Add方法将新的 DataRow 添加到 DataRowCollection 之后,RowState 属性的值设置为 Added。

对于已经使用 Remove 方法(或是在使用 Delete 方法之后使用了 AcceptChanges 方法)从DataRowCollection 中移除的行,也设置为 Detached。

 

 

 

 

3、DataTable成员之DataColumn

 

 DataColumn 表示 DataTable 中列的架构。

 

3.1 DataColumn中常见的熟悉及其说明如下:

 

 

 

属性名

说明

Unique

设置DataColumn对象是否不允许重复的数据

Table 

DataColumn对象所属的DataTable对象

ReadOnly

DataColumn对象是否只读

Ordinal

字段集合中的DataColumn对象顺序

DefaultValue

  DataColumn对象的默认值

DataType

DataColumn对象数据类型

ColumnName

DataColumns集合对象中的字段名称

Count 

DataTable对象中的字段数

Caption

DataColumn对象的标题

AutoIncrement

加入DataRow时,是否自动增加字段

AutoIncrementSeed

DataColumn对象的递增种子

AllowDBNull

DataColumn对象是否接受Null值

 

 

 

3.2 DataColumn.Expression 表达式

获取或设置表达式,用于筛选行、计算列中的值或创建聚合列。表达式的返回类型由列的 DataType 来确定。Expression 属性的一个用途是创建计算出的列。例如,若要计算税值,就要将单价乘以特定地区的税率。由于各地税率不同,不可能将单一税率放在一个列中;于是便用 Expression 属性来计算这个值,如下面这一部分中的 Visual Basic 代码所示:DataSet1.Tables("Products").Columns("tax").Expression = "UnitPrice * 0.086"第二个用途是创建聚合列。类似于计算出的值,聚合基于 DataTable 中的整个行集执行操作。一个简单的示例就是计算该集中返回的行数。这便是您将用来计算特定销售人员所完成的交易数的方法,如下面的 Visual Basic 代码所示:DataSet1.Tables("Orders").Columns("OrderCount").Expression = "Count(OrderID)";

表达式语法

在创建表达式时,使用 ColumnName 属性来引用列。例如,如果一个列的 ColumnName 是“UnitPrice”,而另一个是“Quantity”,则表达式将是:

"UnitPrice * Quantity"

 

 

 

4、DataTable成员之DataView

 

 

DataView类似数据库中的视图。 

DataView 使您能够创建 DataTable 中所存储的数据的不同视图,这种功能通常用于数据绑定应用程序。使用 DataView,您可以使用不同排序顺序显示表中的数据,并且可以按行状态或基于筛选器表达式来筛选数据。

DataView 提供基础 DataTable 中的数据的动态视图:内容、排序和成员关系会实时反映其更改。此行为不同于 DataTable 的 Select 方法,后者从表中按特定的筛选器和/或排序顺序返回 DataRow 数组,虽然其内容反映对基础表的更改,但其成员关系和排序却则保持静态。DataView 的动态功能使其成为数据绑定应用程序的理想选择。
与数据库视图类似,DataView 为您提供了可向其应用不同排序和筛选条件的单个数据集的动态视图。但是,与数据库视图不同的是,DataView 不能作为表来对待,无法提供联接的表的视图。另外,还不能排除存在于源表中的列,也不能追加不存在于源表中的列(如计算列)。

在实际运用中,我们时常使用如下代码:

DataView dv = dt.DefaultView;
dv.Sort = "UserName"; //根据UserName排序,得到新的DataView

DataTable dtNew=dv.ToTable();//将DataView重新转为DataTable

 

4.1 DataViewRowState:

 

其实DataView是类似于DataTable,它里面也有RowState,我们可以使用RowStateFilter来过滤不同状态的行。

 

 

currentRows 包括所有未更新的、新的和修改的数据行
Deleted 所有自上次调用AcceptChanges后删除的数据行
ModifiedCurrent 所有自上次调用AcceptChanges后修改过的数据行
ModifiedOriginal 所有自上次调用AcceptChanges后original版本的数据行
New 所有自上次调用AcceptChanges后新添加的行
OriginalRows 返回初始数据行,包含unchanged和deleted 的
Unchanged 所有未更新的数据行

 

 

 

4.2 DataView的过滤器

    

设置过滤 RowFilter是一个可读写的属性,用来读取和设置表过滤的表达式。public virtual string RowFilter {get; set;}

 你可以用列名,逻辑和数字运算符和常量的任意合法组合组成表达式。以下是一些例子:
dv.RowFilter = "Country = ‘USA‘";
dv.RowFilter = "EmployeeID >5 AND Birthdate < #1/31/82#"
dv.RowFilter = "Description LIKE ‘*product*‘"

让我们来看一下过滤器的基本规则和运算符。
过滤字符串是表达式的逻辑连接。可以用AND,OR,NOT来连接成一个较短的表达式,也可以使用圆括号来组成子句,指定优先的运算。
通常包含列名的子句同字母、数字、日期或另一个列名进行比较。这里,可以使用关系运算符和算术运算符,如>=, <, >, +, *, % (取模)等等。
如果要选取的行并不能方便地通过算术或逻辑运算符表达,你可以使用IN操作符。以下代码显示如何选取一个随机行:
dv.RowFilter = "employeeID IN (2,4,5)"

你也可以使用通配符*和%,它们同LIKE运算符一起使用时显得更有用。它们都表示任意数量的字符,可以相互替代使用。
请注意,如果在LIKE子句中已经有了*或%字符,你必须用方括号将其括起,以免歧义。如果很不幸,字符串中方括号本身也存在了,那么它也必须用将本身括起。这样,匹配语句会如下所示:
dv.RowFilter = "Description LIKE ‘[[]*[]]product[[]*[]]"

通配符只允许在过滤字符串的开头或结尾处使用,而不能在字符串中间出现。例如,下列语句会产生运行时错误:
dv.RowFilter = "Description LIKE ‘prod*ct"

字符串必须以单引号括起,而日期型必须以#符号括起。字符型值可以使用小数点和科学计数法。
RowFilter也支持聚合函数,如SUM, COUNT, MIN,MAX, and AVG。如果表中没有数据行,那么函数将返回NULL。
在介绍RowFilter表达式的最后,让我们讨论三个很便利的函数:Len,IIF和Substring。
正如其名,Len()返回特定表达式的长度。该表达式可以是一个列名,也可以是其他合法的表达式。
Substring()返回指定的表达式自特定位置开始,特定长度的字符子串。
我最喜欢用的是IIF(),它按照逻辑表达式的值有一到两个值。IIF是IF-THEN-ELSE语句的紧凑表达。语法如下:
IIF(expression, if_true, if_false)

通过该函数,可以建立非常复杂的过滤字符串。例如,假定你从SQL Server的Northwind数据库中取得Employees表,下列表达式可以选出那些employeeID小于6且lastname为偶数个字符和employeeID大于6且lastname为奇数个字符的员工。
IIF(employeeID<6, Len(lastname) %2 =0, Len(lastname) %2 >0)

 

4.3 DataView 的排序 

 

DataView支持Sort属性,可以用来对视图中的内容排序。Sort由用逗号分隔的列名表达式进行排序。通过在任何列名后加ASC或者DESC限定词,可以使得字段按照上升或者下降的顺序排列。如果没有方向限定词,默认顺序为ASC。
DataView是内存中的对象,所以排序在本地进行,无需调用数据库服务器。

 

实例篇 

 

实例1.DataTable分组统计数据

 

 

Name

Subject

Scores

Jack

001

90

Jack

002

85.5

Tom

001

78.5

Jerry

001

59

Tom

002

100

 

 

 如上表是dt中的数据,如果要分组进行统计各学生的平均科目成绩,

可以使用如下的方法(当然如果你的.Net版本支持Linq的话,可以使用Linq)

思路:找出所有的学生,遍历表,计算该学生的平均成绩,参考代码如下:

//获取所有的学生
DataView myDataView = new DataView(dt);
string[] strComuns ={ "Name"};
DataTable dtTemp = myDataView.ToTable(true, strComuns);
//新建DataTable存储结构,结构同dt
DataTable  dtNew=dt.Clone();
//根据学生统计数据
for (int i = 0; i < dtTemp.Rows.Count; i++)
{
DataRow drDetail = dtNew.NewRow();
drDetail["Name"] = dtTemp.Rows[i]["Name"].ToString();
drDetail["Scores"] = dt.Compute("AVg(Scores)", "Name=‘" + dtTemp.Rows[i]["Name"].ToString() + "‘");
dtNew.Rows.Add(drDetail);

}

 

代码 

Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->DataTable dt = new DataTable();

            //创建连接
            SqlConnection conn = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["sql2005"].ConnectionString);

            SqlDataAdapter sda = new SqlDataAdapter("select * from test",conn);

            sda.Fill(dt);

            //将得到的数据转成DataView
            DataView dv = new DataView(dt);

            //创建一个包含Name列的字符串数组
            String [] strColumns = { "Name" };

            //将dv视图中不重复的Name列筛选出来
            DataTable dtTemp = dv.ToTable(true,strColumns);

            //克隆表结构给dtNew
            DataTable dtNew = dt.Clone();

            for (int i = 0; i < dtTemp.Rows.Count; i++)
            {
                DataRow drDetail = dtNew.NewRow();
                drDetail["Name"] = dtTemp.Rows[i]["Name"].ToString();
                drDetail["Scores"] = dt.Compute("avg(Scores)", "Name=‘" + dtTemp.Rows[i]["Name"].ToString() + "");
                dtNew.Rows.Add(drDetail);
            }

            GridView1.DataSource = dtNew;
            GridView1.DataBind();

 

 

 

深入详解DataTable