存储过程同时插入两条相同数据 插入时间完全相同

linkinqieerxi 2014-05-27 09:52:57
ALTER PROC [dbo].[Insert]
@Tid Int
AS
BEGIN

IF NOT EXISTS(SELECT 1 FROM Table WHERE TId = @Tid)
BEGIN
INSERT INTO Table (
INSERTDATE,
TID
)
VALUES (
GETDATE(),
@Tid
);
END

END



TID是其他表的主键,主要为了避免重复插入的

这是什么情况。。。

是代码的问题?

求解

...全文
799 36 打赏 收藏 转发到动态 举报
写回复
用AI写文章
36 条回复
切换为时间正序
请发表友善的回复…
发表回复
江南雪_158 2015-08-18
  • 打赏
  • 举报
回复
这应该是高并发引起的,可是使用唯一索引试试
chen870201 2014-05-28
  • 打赏
  • 举报
回复
好奇怪的问题
linkinqieerxi 2014-05-28
  • 打赏
  • 举报
回复
我这边也测试了一下 表中其他列为唯一索引 需要避免重复的列非索引 确实没有再出现重复 学习了 谢谢各位!
發糞塗牆 2014-05-28
  • 打赏
  • 举报
回复
引用 33 楼 linkinqieerxi 的回复:
[quote=引用 31 楼 x_wy46 的回复:] [quote=引用 29 楼 DBA_Huangzj 的回复:] 我个人认为,讨论锁的问题,首先要确定隔离级别,不同的隔离级别对锁的表现不同,虽然排它锁那些是一样的,但是不同的级别对锁的范围和持续时间都有不同的影响,比如可序列化,就有可能加范围锁。你可以看到刚才那篇文档: ROWLOCK 指定通常采用页锁或表锁时,采用行锁。 在从 SNAPSHOT 隔离级别操作的事务中指定时,除非将 ROWLOCK 与需要锁的其他表提示(例如,UPDLOCK 和 HOLDLOCK)组合,否则不会取得行锁。都是要说明在什么隔离级别下的行为。既然你已经有测试结果,大胆相信也是可以的,我也不敢说熟,每次都要打开MSDN或者Books online来看才放心。
我找到答案了,稍后分享出来[/quote]请把答案分享出来吧。[/quote]http://bbs.csdn.net/topics/390798474他已经分享出来了
linkinqieerxi 2014-05-28
  • 打赏
  • 举报
回复
引用 31 楼 x_wy46 的回复:
[quote=引用 29 楼 DBA_Huangzj 的回复:] 我个人认为,讨论锁的问题,首先要确定隔离级别,不同的隔离级别对锁的表现不同,虽然排它锁那些是一样的,但是不同的级别对锁的范围和持续时间都有不同的影响,比如可序列化,就有可能加范围锁。你可以看到刚才那篇文档: ROWLOCK 指定通常采用页锁或表锁时,采用行锁。 在从 SNAPSHOT 隔离级别操作的事务中指定时,除非将 ROWLOCK 与需要锁的其他表提示(例如,UPDLOCK 和 HOLDLOCK)组合,否则不会取得行锁。都是要说明在什么隔离级别下的行为。既然你已经有测试结果,大胆相信也是可以的,我也不敢说熟,每次都要打开MSDN或者Books online来看才放心。
我找到答案了,稍后分享出来[/quote]请把答案分享出来吧。
---涛声依旧--- 2014-05-27
  • 打赏
  • 举报
回复
如果要避免数据重复,建议INSERTDATE,TID联合作唯一索引
exception92 2014-05-27
  • 打赏
  • 举报
回复
同时插入两条相同数据 插入时间完全相同 这不符合正常逻辑吗!!
hujiiori 2014-05-27
  • 打赏
  • 举报
回复
可能调用的代码有bug,连调了两次
xdashewan 2014-05-27
  • 打赏
  • 举报
回复
链接池+高并发?
發糞塗牆 2014-05-27
  • 打赏
  • 举报
回复
改了一下表名,代码没问题,触发器的可能性比较大
--CREATE TABLE test (INSERTDATE datetime,TID int )
create PROC [dbo].[Insert]
    @Tid Int
AS
BEGIN
 
    IF NOT EXISTS(SELECT 1 FROM test WHERE TId = @Tid)
    BEGIN
        INSERT INTO test (
                                                         INSERTDATE,
                                                         TID
                                                     )
                                    VALUES (
                                                        GETDATE(),
                                                         @Tid
                                                     );
         END
 
END



[Insert] 1
SELECT * FROM test

/*
INSERTDATE              TID
----------------------- -----------
2014-05-27 10:20:13.350 1

*/
發糞塗牆 2014-05-27
  • 打赏
  • 举报
回复
检查表上是否有触发器
专注or全面 2014-05-27
  • 打赏
  • 举报
回复
引用 29 楼 DBA_Huangzj 的回复:
我个人认为,讨论锁的问题,首先要确定隔离级别,不同的隔离级别对锁的表现不同,虽然排它锁那些是一样的,但是不同的级别对锁的范围和持续时间都有不同的影响,比如可序列化,就有可能加范围锁。你可以看到刚才那篇文档: ROWLOCK 指定通常采用页锁或表锁时,采用行锁。 在从 SNAPSHOT 隔离级别操作的事务中指定时,除非将 ROWLOCK 与需要锁的其他表提示(例如,UPDLOCK 和 HOLDLOCK)组合,否则不会取得行锁。都是要说明在什么隔离级别下的行为。既然你已经有测试结果,大胆相信也是可以的,我也不敢说熟,每次都要打开MSDN或者Books online来看才放心。
我找到答案了,稍后分享出来
發糞塗牆 2014-05-27
  • 打赏
  • 举报
回复
忘了引用
引用 28 楼 x_wy46 的回复:


--查了一下,说是在锁定键上建立唯一索引(约束)xlock,rowlock才有效

--我测了一下,好像也是的,如下修改存储过程,为了防止还有重复的,UI上也反馈不出来,特意加了个异常的处理,
--如果异常,则记录下来,反反复复测试了即便,2000次循环*30线程,
--结果是预期的,没有重复的
--不知道能否说明这个问题:锁定键上建立唯一索引(约束)xlock,rowlock才有效?

--锁这块一直没弄清楚,我的理解应该分两类吧,范围和锁定方式,
--像tablock,rowlock这些事范围,xlock,updlock这些事锁定方式
--不知道对不对?

drop index index_1 on t


create unique index index_1 on t(id,createdate)


truncate table t


create table logmsg(id int,msg nvarchar(100))

alter proc test_p
@i int
as
begin
	begin try
		begin tran
			if not exists(select 1 from t with(xlock,rowlock)  where id=@i )
			begin 
				insert into t values (@i,GETDATE());
			end
		commit
	end try
	begin catch
		insert into logmsg values (@i,'数据重复异常');
	end catch
end



select COUNT(1),id,Createdate from t
group by id,Createdate
having(COUNT(1))>1


select * from logmsg

[quote=引用 27 楼 DBA_Huangzj 的回复:] 1、我是记得只有tablockx才能锁得住。 2、http://technet.microsoft.com/zh-cn/library/ms187373.aspx中有一句:获取行级别锁的锁提示 ROWLOCK、UPDLOCK 和 XLOCK 可能对索引键而不是实际的数据行采用锁。 [quote=引用 26 楼 x_wy46 的回复:] [quote=引用 24 楼 DBA_Huangzj 的回复:] [quote=引用 23 楼 xdashewan 的回复:] [quote=引用 22 楼 DBA_Huangzj 的回复:] 把判断直接写在insert过程中,算一步,你那个写法算2步
但如果楼主是高并发的情况下,这样会导致数据丢失的吧,高并发产生的数据不一定都是重复无用数据[/quote]不会丢失,只把不存在的数据插入,逻辑来说和你的一样,只是插入的过程直接判断[/quote] 我是这样测试的,如下,写了一个存储过程,用sqlquerystress来开循环和线程
create table t
(
	id int,
	Createdate datetime
)
create  index index_1 on t(id,createdate)

alter proc test_p
@i int
as
begin
	begin tran
		if not exists(select 1 from t with(xlock,rowlock)  where id=@i )
		begin 
			insert into t values (@i,GETDATE());
		end
	commit
end


--执行调用方式
declare @i int
set @i=cast(  rand()*100000 as int)
exec test_p @i	



然后用
select COUNT(1),id,Createdate from t
group by id,Createdate
having(COUNT(1))>1

来判断是否存在重复数据

select不做任何提示的时候,1000循环*30线程重复的很多,就不说了

当select中用(xlock,rowlock)锁提示的时候,竟然还有极个别重复的
用tablockx就没有了

当然你的那种方式也是没有问题的,我稍微改了一下,加了个top 1,如下,要不然是死循环

alter proc test_p
@i int
as
begin
   INSERT  INTO t
     ( id, Createdate )
   SELECT top 1 @i, GETDATE() FROM    test
      WHERE   NOT EXISTS ( SELECT 1 FROM   t WHERE  id = @i )
                                         
end



还请版主指教,为啥
begin tran
		if not exists(select 1 from t with(xlock,rowlock)  where id=@i )
		begin 
			insert into t values (@i,GETDATE());
		end
	commit

这种方式是不行的,还是锁不住?
我在手动测试的时候,第一个窗口
begin tran
		if not exists(select 1 from t with(xlock,rowlock)  where id=12345)
		begin 
			insert into t values (12345,GETDATE());
		end
不提交,同样的脚本,第二个窗口中就被阻塞了,按道理也是没问题的啊

[/quote][/quote][/quote]
發糞塗牆 2014-05-27
  • 打赏
  • 举报
回复
我个人认为,讨论锁的问题,首先要确定隔离级别,不同的隔离级别对锁的表现不同,虽然排它锁那些是一样的,但是不同的级别对锁的范围和持续时间都有不同的影响,比如可序列化,就有可能加范围锁。你可以看到刚才那篇文档: ROWLOCK 指定通常采用页锁或表锁时,采用行锁。 在从 SNAPSHOT 隔离级别操作的事务中指定时,除非将 ROWLOCK 与需要锁的其他表提示(例如,UPDLOCK 和 HOLDLOCK)组合,否则不会取得行锁。都是要说明在什么隔离级别下的行为。既然你已经有测试结果,大胆相信也是可以的,我也不敢说熟,每次都要打开MSDN或者Books online来看才放心。
专注or全面 2014-05-27
  • 打赏
  • 举报
回复


--查了一下,说是在锁定键上建立唯一索引(约束)xlock,rowlock才有效

--我测了一下,好像也是的,如下修改存储过程,为了防止还有重复的,UI上也反馈不出来,特意加了个异常的处理,
--如果异常,则记录下来,反反复复测试了即便,2000次循环*30线程,
--结果是预期的,没有重复的
--不知道能否说明这个问题:锁定键上建立唯一索引(约束)xlock,rowlock才有效?

--锁这块一直没弄清楚,我的理解应该分两类吧,范围和锁定方式,
--像tablock,rowlock这些事范围,xlock,updlock这些事锁定方式
--不知道对不对?

drop index index_1 on t


create unique index index_1 on t(id,createdate)


truncate table t


create table logmsg(id int,msg nvarchar(100))

alter proc test_p
@i int
as
begin
	begin try
		begin tran
			if not exists(select 1 from t with(xlock,rowlock)  where id=@i )
			begin 
				insert into t values (@i,GETDATE());
			end
		commit
	end try
	begin catch
		insert into logmsg values (@i,'数据重复异常');
	end catch
end



select COUNT(1),id,Createdate from t
group by id,Createdate
having(COUNT(1))>1


select * from logmsg

引用 27 楼 DBA_Huangzj 的回复:
1、我是记得只有tablockx才能锁得住。 2、http://technet.microsoft.com/zh-cn/library/ms187373.aspx中有一句:获取行级别锁的锁提示 ROWLOCK、UPDLOCK 和 XLOCK 可能对索引键而不是实际的数据行采用锁。 [quote=引用 26 楼 x_wy46 的回复:] [quote=引用 24 楼 DBA_Huangzj 的回复:] [quote=引用 23 楼 xdashewan 的回复:] [quote=引用 22 楼 DBA_Huangzj 的回复:] 把判断直接写在insert过程中,算一步,你那个写法算2步
但如果楼主是高并发的情况下,这样会导致数据丢失的吧,高并发产生的数据不一定都是重复无用数据[/quote]不会丢失,只把不存在的数据插入,逻辑来说和你的一样,只是插入的过程直接判断[/quote] 我是这样测试的,如下,写了一个存储过程,用sqlquerystress来开循环和线程
create table t
(
	id int,
	Createdate datetime
)
create  index index_1 on t(id,createdate)

alter proc test_p
@i int
as
begin
	begin tran
		if not exists(select 1 from t with(xlock,rowlock)  where id=@i )
		begin 
			insert into t values (@i,GETDATE());
		end
	commit
end


--执行调用方式
declare @i int
set @i=cast(  rand()*100000 as int)
exec test_p @i	



然后用
select COUNT(1),id,Createdate from t
group by id,Createdate
having(COUNT(1))>1

来判断是否存在重复数据

select不做任何提示的时候,1000循环*30线程重复的很多,就不说了

当select中用(xlock,rowlock)锁提示的时候,竟然还有极个别重复的
用tablockx就没有了

当然你的那种方式也是没有问题的,我稍微改了一下,加了个top 1,如下,要不然是死循环

alter proc test_p
@i int
as
begin
   INSERT  INTO t
     ( id, Createdate )
   SELECT top 1 @i, GETDATE() FROM    test
      WHERE   NOT EXISTS ( SELECT 1 FROM   t WHERE  id = @i )
                                         
end



还请版主指教,为啥
begin tran
		if not exists(select 1 from t with(xlock,rowlock)  where id=@i )
		begin 
			insert into t values (@i,GETDATE());
		end
	commit

这种方式是不行的,还是锁不住?
我在手动测试的时候,第一个窗口
begin tran
		if not exists(select 1 from t with(xlock,rowlock)  where id=12345)
		begin 
			insert into t values (12345,GETDATE());
		end
不提交,同样的脚本,第二个窗口中就被阻塞了,按道理也是没问题的啊

[/quote][/quote]
發糞塗牆 2014-05-27
  • 打赏
  • 举报
回复
1、我是记得只有tablockx才能锁得住。 2、http://technet.microsoft.com/zh-cn/library/ms187373.aspx中有一句:获取行级别锁的锁提示 ROWLOCK、UPDLOCK 和 XLOCK 可能对索引键而不是实际的数据行采用锁。
引用 26 楼 x_wy46 的回复:
[quote=引用 24 楼 DBA_Huangzj 的回复:] [quote=引用 23 楼 xdashewan 的回复:] [quote=引用 22 楼 DBA_Huangzj 的回复:] 把判断直接写在insert过程中,算一步,你那个写法算2步
但如果楼主是高并发的情况下,这样会导致数据丢失的吧,高并发产生的数据不一定都是重复无用数据[/quote]不会丢失,只把不存在的数据插入,逻辑来说和你的一样,只是插入的过程直接判断[/quote] 我是这样测试的,如下,写了一个存储过程,用sqlquerystress来开循环和线程
create table t
(
	id int,
	Createdate datetime
)
create  index index_1 on t(id,createdate)

alter proc test_p
@i int
as
begin
	begin tran
		if not exists(select 1 from t with(xlock,rowlock)  where id=@i )
		begin 
			insert into t values (@i,GETDATE());
		end
	commit
end


--执行调用方式
declare @i int
set @i=cast(  rand()*100000 as int)
exec test_p @i	



然后用
select COUNT(1),id,Createdate from t
group by id,Createdate
having(COUNT(1))>1

来判断是否存在重复数据

select不做任何提示的时候,1000循环*30线程重复的很多,就不说了

当select中用(xlock,rowlock)锁提示的时候,竟然还有极个别重复的
用tablockx就没有了

当然你的那种方式也是没有问题的,我稍微改了一下,加了个top 1,如下,要不然是死循环

alter proc test_p
@i int
as
begin
   INSERT  INTO t
     ( id, Createdate )
   SELECT top 1 @i, GETDATE() FROM    test
      WHERE   NOT EXISTS ( SELECT 1 FROM   t WHERE  id = @i )
                                         
end



还请版主指教,为啥
begin tran
		if not exists(select 1 from t with(xlock,rowlock)  where id=@i )
		begin 
			insert into t values (@i,GETDATE());
		end
	commit

这种方式是不行的,还是锁不住?
我在手动测试的时候,第一个窗口
begin tran
		if not exists(select 1 from t with(xlock,rowlock)  where id=12345)
		begin 
			insert into t values (12345,GETDATE());
		end
不提交,同样的脚本,第二个窗口中就被阻塞了,按道理也是没问题的啊

[/quote]
专注or全面 2014-05-27
  • 打赏
  • 举报
回复
引用 24 楼 DBA_Huangzj 的回复:
[quote=引用 23 楼 xdashewan 的回复:]
[quote=引用 22 楼 DBA_Huangzj 的回复:]
把判断直接写在insert过程中,算一步,你那个写法算2步

但如果楼主是高并发的情况下,这样会导致数据丢失的吧,高并发产生的数据不一定都是重复无用数据[/quote]不会丢失,只把不存在的数据插入,逻辑来说和你的一样,只是插入的过程直接判断[/quote]


我是这样测试的,如下,写了一个存储过程,用sqlquerystress来开循环和线程
create table t
(
id int,
Createdate datetime
)
create index index_1 on t(id,createdate)

alter proc test_p
@i int
as
begin
begin tran
if not exists(select 1 from t with(xlock,rowlock) where id=@i )
begin
insert into t values (@i,GETDATE());
end
commit
end


--执行调用方式
declare @i int
set @i=cast( rand()*100000 as int)
exec test_p @i



然后用
select COUNT(1),id,Createdate from t
group by id,Createdate
having(COUNT(1))>1

来判断是否存在重复数据

select不做任何提示的时候,1000循环*30线程重复的很多,就不说了

当select中用(xlock,rowlock)锁提示的时候,竟然还有极个别重复的
用tablockx就没有了

当然你的那种方式也是没有问题的,我稍微改了一下,加了个top 1,如下,要不然是死循环

alter proc test_p
@i int
as
begin
INSERT INTO t
( id, Createdate )
SELECT top 1 @i, GETDATE() FROM test
WHERE NOT EXISTS ( SELECT 1 FROM t WHERE id = @i )

end



还请版主指教,为啥
begin tran
if not exists(select 1 from t with(xlock,rowlock) where id=@i )
begin
insert into t values (@i,GETDATE());
end
commit

这种方式是不行的,还是锁不住?
我在手动测试的时候,第一个窗口
begin tran
if not exists(select 1 from t with(xlock,rowlock) where id=12345)
begin
insert into t values (12345,GETDATE());
end
不提交,同样的脚本,第二个窗口中就被阻塞了,按道理也是没问题的啊





soaringbird 2014-05-27
  • 打赏
  • 举报
回复
引用 5 楼 duanzi_peng 的回复:
同时插入两条相同数据 插入时间完全相同 这不符合正常逻辑吗!!
那个时间精度才到毫秒,一个毫秒内很多条数据完全是有可能的
發糞塗牆 2014-05-27
  • 打赏
  • 举报
回复
引用 23 楼 xdashewan 的回复:
[quote=引用 22 楼 DBA_Huangzj 的回复:] 把判断直接写在insert过程中,算一步,你那个写法算2步
但如果楼主是高并发的情况下,这样会导致数据丢失的吧,高并发产生的数据不一定都是重复无用数据[/quote]不会丢失,只把不存在的数据插入,逻辑来说和你的一样,只是插入的过程直接判断
xdashewan 2014-05-27
  • 打赏
  • 举报
回复
引用 22 楼 DBA_Huangzj 的回复:
把判断直接写在insert过程中,算一步,你那个写法算2步
但如果楼主是高并发的情况下,这样会导致数据丢失的吧,高并发产生的数据不一定都是重复无用数据
加载更多回复(16)

27,579

社区成员

发帖
与我相关
我的任务
社区描述
MS-SQL Server 应用实例
社区管理员
  • 应用实例社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧