sqlserver数据库主键的生成方法有哪些?针对这个问题,本文详细介绍了相应的分析和解决方法,希望能帮助更多想要解决这个问题的小伙伴找到一个更简单易行的方法。
生成主键有三种主要方式:
一、自动生成数据库
二.全局唯一标识符
三.发展和创造
严格来说,这三种制作方法有一定的交集,下面将对定位方法进行说明。
第一种方式,主要定位在自生长识别种子:可以设置起始值和生长步长。优点是并发任务完全由数据库引擎管理,所以当多个用户使用它们时,您不必担心两个相同的id。这也是缺点。大多数数据库不提供直接获取身份标识的方法。生成ID的方式对开发者来说是透明的,开发者很难介入这一项。数据迁移不是很方便。由于上述的优缺点,这种自生长的ID一般用于设计基本表的主键(系统运行的基本信息,如员工表),但很少(完全没有)用于主从表的主、外键,因为必须先确定主表的ID,才能定位从表的关联ID。
示例(MsSQL):
代码如下:
-创建测试表create table[identity](id int identity(1,2)不是null主键,
-种子的起始值为1,步长为2数字varchar (20)唯一不为null,名称varchar (20)不为null,密码varchar (20)默认为(123),描述varchar (40)为null)
-将记录插入插入到[标识](编号、名称、描述)值中(‘001’、‘1st’、ID=1,因为起始值为1’)
插入[标识](编号、名称、描述)值(' 002 ',' 2nd ',' ID=3,因为起始值为1,步长为2 ')
插入[标识](编号、名称、描述)值(' 003 ',' 3rd ',' Id=5,由于字符长度过长,错误插入失败,导致此Id在生成后被放弃')
插入[标识](编号、名称、描述)值(' 004 ',' 4th ',' ID=7 not5,因为第三条记录未能插入')
-检索记录并查看结果选择*从[身份]
结果:(1行受影响)(1行受影响)消息8152,级别16,状态14,第3行将截断字符串或二进制数据。语句已被终止。(1行受影响)(3行受影响)ID号名称密码描述1 001st 123id=1,因为起始值1 3 002nd 123id=3,因为起始值1,步长2 7 004 4th 123 Id=7不是5,因为第三条记录未能插入,第二个方法GUID也称为UUID(通用唯一标识符)。全球唯一标识符(GUID)通常由32位十六进制值组成,包括网卡地址、时间和其他信息。没有两台计算机会产生相同的GUID。它的优势在于它的独特性,在需要数据库集成时可以节省大量的人力。比如总部和分支机构的系统独立运行,分支机构的所有数据都需要定期提交给总部,这样可以避免数据合并时主键的冲突。同时,GUID还具有自生长识别种子的特性,不需要开发者过多关注。但是GUID信息量大,占用空间大,关联检索时估计效率不是很高,32位十六进制的可读性也很差。虽然主键对用户来说没有意义,但在设计或调试通信时非常不方便。从长远来看,为了保证数据的可移植性,通常选择GUID作为主键。
示例(MsSQL):
代码如下:
-创建测试表创建表guid (id唯一标识符不为空主键,
-当然,您也可以使用字符串来保存数字varchar (20)唯一不为空,名称varchar (20)不为空,密码varchar (20)默认(123))
-插入记录插入guid (id,编号,名称)值(新id(),' 001 ',' 1st ')
插入GU
ID(Id,Number,Name) VALUES(NewID(),'002','2nd')
INSERT INTO GUID(Id,Number,Name) VALUES(NewID(),'003','3rd')
--检索记录,查看结果 SELECT * FROM GUID
结果: Id Number Name Password 8E194F55-B4D3-4C85-8667-33BC6CD33BBC 001 1st 123 7141F202-7D0E-4992-9164-5043EC9FC6F6 002 2nd 123 E0E365A0-8748-4656-AF24-5D0B216D2095 003 3rd 123 第三种方式开发创建,其便捷性在于可控制性,此可控制性是指其组成形式,可以是整形、也可以是字符型,你可以根据实际情况给予多样的组成及产生形式,说到这里可能有的朋友就想起来自动产生单号,如:20120716001或者PI-201207-0001等等,没错,自我创建同样适用于这些类似的应用。 说到自我创建,多数首先想到的是取Max(Id)+1,这种方式虽然省事,但是实际上对于定制(在生产单号之类的有一定意义的信息时可能会有这样的需求,主键没必要)及并发的处理并不是很好。如,当前表中最大编号为1000,当C1和C2用户同时取这个Id处理时,得到的都是1001,导致保存失败。常规的做法是在取值时候加锁,但是当多用户频繁操作时,性能是个很大的问题,其中主要的原因之一是直接操作的业务数据表。 针对此种情况,解决方案是使用键值表来保存表名、当前或者下一个Id及其他信息,如果系统中多个表Id都使用这种方式,那么键值表中就会有多条相应的规则记录;当然也可以让整个数据库所有表的Id从都按相同的规则从一个源产生,那么键值表中只需要一条规则记录即可。
下面来看看这样一个使用键值表例子的演变(MsSQL):
代码如下:
--创建键值表 CREATE TABLE KeyTable( ID INT IDENTITY(1,1) PRIMARY KEY NOT NULL,
TCode VARCHAR(20) UNIQUE NOT NULL,
TName VARCHAR(50) NOT NULL,
TKey INT NOT NULL, )
GO
--插入测试记录 INSERT INTO KeyTable(TCode,TName,TKey) VALUES('T001','Test',0) GO
--创建获取指定表ID的存储过程,也可以修改成函数 CREATE PROCEDURE UP_NewTableID @TCode VARCHAR(20),@NextID INT OUTPUT AS DECLARE @CurTKey INT,
@NextTKey INT BEGIN TRAN TransID SELECT @CurTKey=TKey FROM KeyTable
WHERE TCode = @TCode IF @@ROWCOUNT = 0 BEGIN ROLLBACK TRAN TransID RAISERROR('Warning: No such row is exists',16,1) RETURN END SET
@NextTKey = @CurTKey + 1 --WAITFOR DELAY '00:00:05' UPDATE KeyTable SET TKey = @NextTKey
WHERE TCode = @TCode IF @@ROWCOUNT = 0 BEGIN ROLLBACK TRAN TransID RAISERROR('Warning: No such row is updated',16,1) RETURN END COMMIT TRAN TransID SET @NextID = @NextTKey GO
执行存储过程UP_NewTableID
代码如下:
DECLARE @NextID INT EXEC UP_NewTableID 'T001',
@NextID OUTPUT PRINT
@NextID运行的时会发现很正常,获取的结果也很正确。但是如果在高并发的情况,多个用户可能就会获取相同的ID,如果获取的ID后是用于保存对应表中的记录,那么最多只有一个用户能保存成功。
下面模拟一下并发情形,将上面的存储过程UP_NewTableID中语句WAITFOR DELAY '00:00:05'的注释去掉,打开3个查询分析器的窗体,依次执行上面语句。 预期是想分别获得1,2,3,但是也许会发现多个窗体的运行结果都是:1。这就是说在更新语句执行之前,大家都获取的ID是0,所以下一个数值都是为1。(实际的数值,根据DELAY的参数大小及运行时间按间隔有关) 从这方面来分析的话有的朋友可能就会想到,是否可以在更新语句执行时判断ID是不是原始ID了?
修改过程:
代码如下:
ALTER PROCEDURE UP_NewTableID @TCode VARCHAR(20),
@NextID INT OUTPUT AS DECLARE @CurTKey INT,
@NextTKey INT BEGIN TRAN TransID SELECT
@CurTKey=TKey FROM KeyTable WHERE TCode=@TCode IF @
@ROWCOUNT=0BEGIN ROLLBACK TRAN TransID RAISERROR('Warning: No such row is exists',16,1) RETURN END SET @NextTKey=@CurTKey+1 WAITFOR DELAY '00:00:05' UPDATE KeyTable SET TKey=@NextTKey WHERE TCode=@TCode AND TKey=@CurTKey--此处加上TKey的校验 IF @@ROWCOUNT=0BEGIN ROLLBACK TRAN TransID RAISERROR('Warning: No such row is updated',16,1) RETURN END COMMIT TRAN TransID SET @NextID=@NextTKey GO
如果打开个3个执行过程来模拟并发,那么会有2个窗体出现: 消息 50000,级别 16,状态 1,过程 UP_NewTableID,第 28 行 Warning: No such row is updated 由此会看到还是会由于并发导致有用户操作失败,但是较上一个至少将错误出现的时间点提前了。 那么有没有更好的方法,从查询到更新结束整个事务过程中,不会有任何其他事务插入其中来搅局的办法呢,答案很明确,有,使用锁!需要选择适当的锁,否则效果将和上面的一样。
代码如下:
ALTER PROCEDURE UP_NewTableID
@TCode VARCHAR(20),@NextID INT OUTPUT AS DECLARE
@CurTKey INT,@NextTKey INT BEGIN TRAN TransID SELECT
@CurTKey=TKey FROM KeyTable WITH (UPDLOCK)
--采用更新锁,并保持到事务完成 WHERE TCode=@TCode IF @@ROWCOUNT=0BEGIN ROLLBACK TRAN TransID RAISERROR('Warning: No such row is exists',16,1) RETURN END SET
@NextTKey=@CurTKey+1 WAITFOR DELAY '00:00:05'
UPDATE KeyTable SET TKey=@NextTKey WHERE TCode=@TCode
--此处无需验证TKey是否与SELECT的相同 COMMIT TRAN TransID SET @NextID=@NextTKey GO
可以打开N(N>=2)个窗体来进行测试,将会看到所有操作都被串行化,结果就是我们想要的那样。如此注释或者去掉模仿并发的语句WAITFOR DELAY '00:00:05'即可。
如前面所说,这同样适应于单据编号类似编码的产生形式,只要对前面的代码及键值表稍作修改即可,有兴趣的朋友可以一试。如果是从前端取得这个编号,并应用于各个记录,那么可能存在跳号的可能。如果为了保证不存在跳号,一种解决方案就是使用跳号表,将跳号记录定期扫描并应用于其他记录。另一种解决方案是将记录的保存操作放置到编号产生的过程中,形成一个串行化的事务。
关于sqlserver数据库主键的生成方式有哪些问题的解答就分享到这里了,希望
内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/128951.html