首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 数据库 > SQL Server >

sqlserver 主键生成策略的有关问题

2012-12-25 
sqlserver 主键生成策略的问题想问问各位大侠 你们用sqlserver的时候 是怎么生成主键的我这里的话 表的主

sqlserver 主键生成策略的问题
想问问各位大侠 你们用sqlserver的时候 是怎么生成主键的
我这里的话 表的主键是用存储过程生成 一般是
日期+6位号 比如今天的生成应该是121023000001 ,121023000002 ,121023000003 等,到了明天就是 121024000001,121024000002等
每个表都是这样通过一个存储过程来生成主键,但是随着并发量的增加 程序就会出现 阻塞、死锁等问题。
有时候还会丢数据 这个是非常大单的。
现在有没有什么好的其他办法。
我能想到的 1 换数据库 sql2000 升级到 sql2005或 2008 
           2 换新的服务器 我这个是IBM 3650 志强2.2G 8G内存 
           3 根本上换这个主键生成的策略 
              因为生成主键的时候 是要锁表的,一旦并发,就问题来了。如果改成系统自动获取主键不知道行不行,是不是就没有 阻塞、死锁 这些问题了。


           现在头都大了 麻烦各位高手指点迷津!谢谢!

[最优解释]
通过升级版本来完全避免死锁等问题是不现实的,由于关系数据库需要保证数据一致性,所以等待甚至阻塞是无法避免的。

主键上的日期部分可以通过convert/cast对getdate来转换得到。后面那些自增的标识,可以通过存储过程里面的IDENT_CURRENT/SCOPE_IDENTITY函数来获取最大,然后加1.

http://blog.csdn.net/dba_huangzj/article/details/7685162
[其他解释]

引用:
通过升级版本来完全避免死锁等问题是不现实的,由于关系数据库需要保证数据一致性,所以等待甚至阻塞是无法避免的。

主键上的日期部分可以通过convert/cast对getdate来转换得到。后面那些自增的标识,可以通过存储过程里面的IDENT_CURRENT/SCOPE_IDENTITY函数来获取最大,然后加1.

http://blog.csdn.net/dba_huangzj/artic……



CREATE PROCEDURE CreateID 
@TableName varchar(50) 

AS
begin   
    declare @LastID varchar(50)
    declare @CreateID varchar(12) 
    SET LOCK_TIMEOUT 3600
    
    begin TRANSACTION
        select @LastID = CreateID from CreateID with (rowlock)  where TableName = @TableName  
        if @@ERROR<>0
        begin
            ROLLBACK TRANSACTION
            set @CreateID=''
            RETURN
        end
        set @CreateID =convert(char(6),getdate(),12)+'000001'
        if @LastID is null
            begin
                set @CreateID=convert(char(6),getdate(),12)+'000001'
                insert CreateID (TableName,CreateID) values( @TableName,@CreateID)
            end 
        else
            begin
           if left(@LastID,6)  = convert(char(6),getdate(),12)
                  begin
                set @CreateID=convert(char(6),getdate(),12)+replace(str(substring(@LastID,7,6) + 1,6),' ','0')


               end 
                update CreateID set CreateID=@CreateID where TableName = @TableName
            end
        if @@ERROR<>0
            begin
                ROLLBACK TRANSACTION 
                set @CreateID=''
                RETURN
            end
    commit TRANSACTION
     select @CreateID as CreateID

end

GO
楼上的高手 帮我看看我这个生成主键的存储过程有什么问题吗?
[其他解释]
第一个问题,没有注释,第二个问题,貌似太复杂了,事务太多的话容易阻塞,可以考虑把整个操作放到一个事务里面去
[其他解释]
引用水哥的

-->Title:得到普通流水编号的函数
-->Author:wufeng4552
-->Date :2009-10-20 
if object_id('tb','U')is not null drop table tb
if exists(select * from dbo.sysobjects where id=object_id(N'[dbo].[f_GetBH]')and xtype in(N'FN', N'IF', N'TF'))
drop function [dbo].[f_GetBH]
GO
create function f_GetBH()
returns varchar(8)
as
  begin
     return(select right(1000001+isnull(right(max(BH),6),0),6) from tb with (xlock,paglock) )
  end
go
--建表
create table tb(BH varchar(8) primary key default dbo.f_GetBH(),ID int)
insert tb(ID) select 1
insert tb(ID) select 2
insert tb(ID) select 3
delete tb where id=2
insert tb(ID) select 4
select * from tb
/*
BH       ID
-------- -----------
000001   1
000003   3
000004   4

*/

-->Title:得到日期编号的函数
-->Author:wufeng4552
-->Date :2009-10-20 
if object_id('tb','U')is not null drop table tb
if exists(select * from dbo.sysobjects where id=object_id(N'[dbo].[f_GetRQBH]')and xtype in(N'FN', N'IF', N'TF'))
drop function [dbo].[f_GetRQBH]
GO
create function f_GetRQBH()
returns varchar(10)
as
  begin
     declare @dt varchar(6)
     select @dt=convert(varchar(6),getdate(),12)
     return
    (select @dt+right(1000001+isnull(right(max(BH),3),0),3) from tb with (xlock,paglock) where bh like @dt +'%')
  end
go
--建表
create table tb(BH varchar(10) primary key default dbo.f_GetRQBH(),ID int)
insert tb(ID) select 1
insert tb(ID) select 2
insert tb(ID) select 3
delete tb where id=2
insert tb(ID) select 4
select * from tb


/*
BH         ID
---------- -----------
091020001  1
091020003  3
091020004  4
*/
-->Title:使用编号表生成流水号
-->Author:wufeng4552
-->Date :2009-10-20 
IF OBJECT_ID('TB_NO','U')IS NOT NULL DROP TABLE TB_NO
GO
CREATE TABLE tb_NO(
Name char(2) PRIMARY KEY,               --编号种类的名称
Head nvarchar(10) NOT NULL DEFAULT '',  --编号的前缀
CurrentNo int NOT NULL DEFAULT 0,       --当前编号
BHLen int NOT NULL DEFAULT 6,           --编号数字部分长度
DESCRIPTION NVARCHAR(50))               --编号种类说明
INSERT tb_NO SELECT 'CG','CG',1,4,N'采购订单'
UNION  ALL   SELECT 'CJ','CJ',1,4,N'采购进货'
UNION  ALL   SELECT 'JC','JC',1,4,N'进仓单'
UNION  ALL   SELECT 'ZC','ZC',1,4,N'转仓单'
UNION  ALL   SELECT 'CC','CC',1,4,N'出仓单'
GO
IF OBJECT_ID('P_NEXTBH','P')IS NOT NULL DROP PROC P_NEXTBH
GO
CREATE PROC p_NextBH
@Name char(2),           --编号种类
@BH nvarchar(20) OUTPUT  --新编号
AS
BEGIN TRAN
    UPDATE tb_NO WITH(ROWLOCK) SET 
        @BH=convert(char(8),getdate(),112)+RIGHT(POWER(10,BHLen)+CurrentNo,BHLen),
        CurrentNo=CurrentNo+1
    WHERE Name=@Name
COMMIT TRAN
GO
--获取 CJ 的新编号
DECLARE @bh char(11)
EXEC p_NextBH 'jc',@bh OUT
SELECT @bh
/*

------
CJ0001
*/
s


[其他解释]
主键生成 最好用完整时间的方法 ,比如 2012-10-23 10:11:12.001 可以生成20121023101112001 这样就不会重 且速度很快 不是阻塞及死锁。用一个存储过程来完成主键生成 处理这个时间序列即可。
[其他解释]
引用:
第一个问题,没有注释,第二个问题,貌似太复杂了,事务太多的话容易阻塞,可以考虑把整个操作放到一个事务里面去


CREATE PROCEDURE CreateID  
@TableName varchar(50)   --输入参数 表的名 我的每个表通过这个获得主键 

AS
begin   
  declare @LastID varchar(50)
  declare @CreateID varchar(12)  
  SET LOCK_TIMEOUT 3600
    
  begin TRANSACTION
  select @LastID = CreateID from CreateID with (rowlock) where TableName = @TableName    --获取CreateID表中 当前表的最新值
  if @@ERROR<>0  --如果异常 回滚
  begin   
  ROLLBACK TRANSACTION 
  set @CreateID=''
  RETURN
  end   -- 回滚结束
  set @CreateID =convert(char(6),getdate(),12)+'000001' --获得当前表的第一条记录
  if @LastID is null   --如果是新表 则生成createid表一条新的记录
  begin
  set @CreateID=convert(char(6),getdate(),12)+'000001'


  insert CreateID (TableName,CreateID) values( @TableName,@CreateID)
  end   -- 生成新记录结束
  else
  begin  -- 如果不是新当天第一条记录 则原记录 + 1
  if left(@LastID,6) = convert(char(6),getdate(),12)  
  begin
  set @CreateID=convert(char(6),getdate(),12)+replace(str(substring(@LastID,7,6) + 1,6),' ','0')
  end  
  update CreateID set CreateID=@CreateID where TableName = @TableName --更新表的当前记录
  end
  if @@ERROR<>0
  begin
  ROLLBACK TRANSACTION  
  set @CreateID=''
  RETURN
  end
  commit TRANSACTION
  select @CreateID as CreateID -- 输出给程序调用


[其他解释]
end   -- 回滚结束
   set @CreateID =convert(char(6),getdate(),12)+'000001' --获得当前表的第一条记录
我觉得你这一步应该不用吧.反正你下面都判断了,
[其他解释]
--种子表 num_tb 
if object_id('num_tb') is not null
drop table num_tb
go
create table num_tb(d datetime,id int)
insert num_tb select getdate(),1 
if object_id('tb') is not null
drop table tb
go
create table tb(id varchar(20),name varchar(10)) 

create clustered index idx_clu_tb on tb(id) 
go 

create trigger tri_tb on tb 
INSTEAD OF INSERT 
as 
begin 
set nocount on 
declare @i int,@id varchar(20),@j int 
select @i=count(*) from inserted 
begin tran 
update num_tb with(TABLOCKX) set 
id=(case when convert(char(8),d,112)=convert(char(8),getdate(),112) 
then id+@i else @i end), 
@j=(case when convert(char(8),d,112)=convert(char(8),getdate(),112) then id else 0 end), 
d=getdate() 
commit tran 
select * into #t from inserted 
update #t set id=convert(varchar(8),getdate(),112)+right('00000'+rtrim(@j),5),@j=@j+1 
insert tb select * from #t 
end 
go 




--插入记录测试 

insert into tb(name) values('张三') 
insert into tb(name) values('李四') 
select * from tb
/*
id                   name
-------------------- ----------
2010012700002        张三
2010012700003        李四
*/
insert into   tb select '2010012700003','王五'
/*
id                   name
-------------------- ----------
2010012700002        张三
2010012700003        李四
2010012700004        王五

*/

热点排行