通常,给数据库中的表都添加一个“无意义”的主键,能够大大地简化程序的开发。这个主键用什么类型呢?其实各种类型,只要大小不超过900字节都可以,但我们最常面临的两种选择是——GUID(UniqueIdentifity)和Identity INT。
《ADO.NET 2.0高级编程》一书的“5.2.2 选择主键”一节,对此进行了一些对比,并推荐使用GUID类型作为主键的类型。但本文中老刘将介绍自己在实际开发中的一些感触。
老刘在公司写程序时,面对的数据库绝大多数都是用Identity做主键;而回家自己写程序玩的时候,因为迷信《ADO.NET 2.0高级编程》,所以设计的数据库都采用GUID做主键类型。所以,对这两种主键类型都有些想法。
GUID优于Identify的方面
首先,对于GUID类型的主键,通常在创建新的数据条目时可以采用Guid.NewGuid()方法为其生成主键;而对于Identify类型的主键,只有当数据条目被真正插入数据库后才能得到其主键值。当需要在同一个事务中,向多个相关表中插入数据时,若采用GUID主键,就能使用一次提交动作插入所有数据;而如果使用Identify类型做主键,就必须先插入主键列所在表的数据,再插入外键列所在表的数据。如下面两段代码所示。
using(MyDataContext db = new MyDataContext())
{
Category c = new Category
{
Id = Guid.NewGuid(),
Name = "分类1"
};
Article a = new Article
{
Id = Guid.NewGuid(),
Title = "标题1",
Body = "内容",
CategoryId = c.Id // Category对象的Id已经确定
};
db.Categories.InsertOnSubmit(c);
db.Articles.InsertOnSubmit(a);
db.SubmitChanges(); // 一次提交,如果出现错误自动全部回滚
}
// 使用Identity作为主键类型
using(MyDataContext db = new MyDataContext())
{
Category c = new Category // 由于才用Identity类型作为主键,所以无需也不能明确指定主键值
{
Name = "分类2"
};
db.Categories.InsertOnSubmit(c);
db.SubmitChanges(); // 提交后才能得到确定的ID值
Article a = new Article
{
Title = "标题又来了",
Body = "没有内容",
CategoryId = c.Id
};
db.Articles.InsertOnSubmit(a);
db.SubmitChanges(a); // 第二个提交,如果出错,分类c也会留在数据库中
}
正如注释中所述,当发生错误时,一次提交可以简单干净地回滚;但多次提交,就得采取一些手段了。
Identify优于GUID的方面
首先,Identify根本上说是一个int,只占4字节;而GUID要占16字节。这当然不算什么,可以忽略。
其次,要知道,主键就是一个簇索引(聚集索引),这意味着数据在磁盘上的物理排列顺序是和主键顺序一样的。这就存在一个问题,每次生成的GUID并不是按照时间顺序从小到大的,换句话说,第二次生成的GUID可能比第一次小,而第三次可能又比第二次小。这就导致每次插入一行数据时,都要对表中整行整行的数据进行排序。而Identify类型的字段,可以明确保障后生成的值比之前生成的值都大,因此新插入的数据会简单地追加在表的尾部。所以,对于频繁插入新数据的表来说,Identify主键的性能要好一些。(注意,本条纯属老刘推测,没有进行过测试。)
最后,Identify主键更便于人类阅读。嗯……给大家各场景吧。在公司,我没有查看生产环境数据库的权限,所以当客户提交来bug之后,我会朝我老板喊:帮我把服务器上ID是1234的用户的数据取出来我看看~~ 想想吧,如果采用了GUID主键,我该怎么喊?
-----
注,写到这里,就完了。但我发现好像有些倾向于Identify主键。希望大家不要有这样的偏见,好好看看《ADO.NET 2.0高级编程》,考虑考虑。欢迎大家在这里探讨。
转自:http://www.cnblogs.com/AndersLiu/archive/2008/07/03/primer-key-guid-vs-identify-int.html
GUID(Global unique identifier)全局唯一标识符,它是由网卡上的标识数字(每个网卡都有唯一的标识号)以及 CPU 时钟的唯一数字生成的的一个 16 字节的二进制值。
2)使用 T-SQL
cmd.CommandText = "SELECT NewID()";
string rowID = (string) cmd.ExecuteScalar();
cmd.CommandText = "INSERT INTO Table(ID,...) VALUES(@ID,...)
cmd.Parameters.Add("@ID",SqlDbType.UniqueIdentifier).Value = new Guid(rowID);
cmd.ExecuteNoQuery();
3、GUID 的优缺点
- 同 IDENTITY 列相比,uniqueidentifier 列可以通过 NewID() 函数提前得知新增加的行 ID,为应用程序的后续处理提供了很大方便。
- 便于数据库移植,其它数据库中并不一定具有 IDENTITY 列,而 Guid 列可以作为字符型列转换到其它数据库中,同时将应用程序中产生的 GUID 值存入数据库,它不会对原有数据带来影响。
- 便于数据库初始化,如果应用程序要加载一些初始数据, IDENTITY 列的处理方式就比较麻烦,而 uniqueidentifier 列则无需任何处理,直接用 T-SQL 加载即可。
- 便于对某些对象或常量进行永久标识,如类的 ClassID,对象的实例标识,UDDI 中的联系人、服务接口、tModel标识定义等。
- GUID 值较长,不容易记忆和输入,而且这个值是随机、无顺序的,所以使用时要注意场合,最好不要尝试用它来作为你的电子邮件地址 J
- GUID 的值有 16 个字节,与其它那些诸如 4 字节的整数相比要相对大一些。这意味着如果在数据库中使用 uniqueidentifier 键,可能会带来两方面的消极影响:存储空间增大;索引时间较慢。
转自:http://blog.csdn.net/xfworld/article/details/1483308
ACCESS中使用GUID全局唯一标识符的自动唯一编号[同步复制ID]之解决方法 http://blog.csdn.net/johnsuna/article/details/2322001
背景:
这段时间临时为一个旅游类网站制作一些网站程序。数据表的情况大致如下: 图1 数据库表的大致情况 由于是Access数据库,之前有两个数据表:TC_TourCompany和TC_SubDetail,前者是旅行社名录相关资料(为了方便描述,暂且叫“总公司表”),后者是下属营业部(如果有的话)的相关资料(为方便描述,暂且叫“子公司表”)。
由于业务需要,想将之扩展为适用于所有“公司类”(比如酒店、景区、景点、漂流公司、娱乐餐饮、机票代理、交通公司等)的数据表,由于酒店、餐饮娱乐、机票代理等公司都有可能有分部或分公司,所以表的数据结构是差不多的。所以,我们可以通用这样的数据表设计来简化今后的程序开发。当然,我们需要在数据表中新增一列,用于描述公司的类型是旅行社、酒店、景区或是娱乐餐饮类公司等。不在本文的叙述范围,按下不表。
为了方便今后的分类搜索查询,确保公司(包括子公司)的唯一性,所以,我想在上述两个表中增加一列,我把列名叫做GUID。它的每条记录都是唯一不重复的值,类似:{9E4038C8-E965-45B1-BDE1-9F06E6B280A3},这有点象.Net中的System.Guid.NewGuid()生成的值,并用大括号{}包含起来。
做法: 如何在已有数据库表记录的情况下自动生成每一条记录的这些值呢?
一开始,我走了点弯路。在新增GUID列时,我选择了此列的数据类型为“数字”并在下面常规选项卡中“字段大小”中选择了“同步复制 ID”,索引中选择了“有(无重复)”。本以为这样保存结构之后就万事大吉,最终打开表的所有记录时发现,GUID列完全为空,没有任何值!于是,我想了一些办法去插入GUID唯一值。方案之一是在ACCESS中使用SQL语句更新,后来发现此路不通。方案之二就是使用ADO.net编程方式更新表记录,工作量也不小。
有没有更好的办法呢?一个偶尔的想法让我找到了更快更好的解决办法,那就是在设计视图中建立GUID列时,数据类型选择自动编号而不是数字!同时,在下面常规选项卡中“字段大小”中选择了“同步复制 ID”,索引中选择了“有(无重复)”。
如下图: 图2 给总公司名录表(TC_TourCompany表)增加GUID列
图3 给总公司表(TC_TourCompany表)增加GUID列后自动生成GUID记录值 图4 给分公司(分部)TC_SubDetail表增加GUID列
图5 给分公司(分部)TC_SubDetail表增加GUID列后自动生成GUID记录值
以后新增记录时会发生什么?经测试发现,ACCESS会自动搞定生成GUID记录值的问题。OK,完美!
更多的话: 从 Access 生成 SQL 语句时,遇到了 Guid 查询的问题,在 SQL Server 中使用的字符串形式,不能查询出任何数据。
如果条件字符串所引用的列为 GUID 类型,那么该条件表达式使用的语法稍微有所不同: WHERE [GUID] ={GUID {12345678-90AB-CDEF-1234-567890ABCDEF}} 请确保包含如上所示的嵌套大括号和连字号。 需要注意的是,嵌入大括号的方法只用于 Where 语句,在 Insert 语句中还是要使用单引号,否则将产生MALFORMED GUID in query 的错误。
更多参考: ASP.NET开发经验(3) --- 使用 GUID 值来作为数据库行标识 http://blog.joycode.com/moslem/archive/2004/03/23/16930.aspx
其他: 导出/打印Access数据库的结构 http://blog.csdn.net/johnsuna/archive/2008/05/05/2393664.aspx
附录:
Access数据类型与.net OleDbType枚举类型的对应
最常见的数据类型映射列表
访问类型名称 | 数据库数据类型 | OLEDB 类型 | .NET 框架类型 | 成员名称 |
文本 | VarWChar | DBTYPE _ WSTR | System.String | OleDbType.VarWChar |
备忘录 | LongVarWCha R | DBTYPE _ WSTR | System.String | OleDbType.LongVarWChar |
字节数: | UnsignedTinyInt | DBTYPE _ UI 1 | System.Byte | OleDbType.UnsignedTinyInt |
是/否 | Boolean | DBTYPE_BOOL | System.Boolean | OleDbType.Boolean |
日期 / 时间 | DateTime | DBTYPE _ DATE | System.DateTime | OleDbType.date |
货币 | 十进制 | DBTYPE_NUMERIC | System.Decimal | OleDbType.numeric |
十进制数: | 十进制 | DBTYPE_NUMERIC | System.Decimal | OleDbType.numeric |
双精度数字: | 双精度数字 | DBTYPE_R8 | System.Double | OleDbType.Double |
自动数字(复制 ID) | GUID | DBTYPE_GUID | System.Guid | OleDbType.guid |
复制 (ID) 号: | GUID | DBTYPE_GUID | System.Guid | OleDbType.guid |
自动数字(长整型) | 整数 | DBTYPE_I4 | System.Int 32 | OleDbType.integer |
数量: (长整型) | 整数 | DBTYPE_I4 | System.Int 32 | OleDbType.integer |
OLE 对象 | LongVarBinary | DBTYPE_BYTES | 数组 System.Byte | OleDbType.LongVarBinary |
单精度数字: | 单精度数字 | DBTYPE_R4 | System.Single | OleDbType.single |
整型数: | SmallInt | DBTYPE_I2 | System.Int 16 | OleDbType.SmallInt |
二进制 | VarBinary * | DBTYPE_BYTES | 数组 System.Byte | OleDbType.binary |
超链接 | VarWChar | DBTYPE _ WSTR | System.String |
OleDbType.VarWChar |
________________________________________________________________
string str = Guid.NewGuid().ToString("B"); //生成guid,并用{}包括guid值