SQL Server 版人气不旺,来这里再发一次,有关行级锁的问题,新手向大家请教了
本人在处理并发操作的时候碰到了问题
有表结构如下
Code(varchar) Content(text)
001 **********
002 **********
003 **********
其中Content字段存放的是一设计好的XML文件,到时候只需要将其读出写入XML中,应用程序就可以直接处理了。
但目前想实现的情况如下:
当用户正在读取Code='001'的时候,就想自动将001该行锁定,其他用户可读,但不可写,并且需要反馈出哪个用户用的哪台主机正在进行读操作
问题
1、需要行锁,如果使用Select Code,Content From Table1 with (ROWLOCK) Where Code = '001',这样操作是否正确?(想实现该行不能进行写操作,如果有写入的操作,就报错)
2、如何解锁
3、如何读取出某一行正被哪个用户进行操作?
谢谢!
原贴位置:
http://community.csdn.net/Expert/topic/4661/4661107.xml?temp=.6548883
问题点数:200、回复次数:11Top
1 楼Modest(塞北雪貂)·(偶最欣赏楼主的分)回复于 2006-04-04 13:55:40 得分 0
除了行锁定外我只想到一个办法,就是给数据库加几个字段LockUserID、Locked、LockTime,select的同时更新这些记录的LockUserID、Locked、LockTime,在规定的时限内只有LockUserID可以更新。
所有用户select前先将LockTime和getdate()比较,超过时限的先解锁,再select资料出来。Top
2 楼Modest(塞北雪貂)·(偶最欣赏楼主的分)回复于 2006-04-04 13:56:05 得分 90
--锁定记录,只允许单用户修改的例子:
--创建测试环境
--创建测试表--部门表
create table 部门(departmentid int,name varchar(10))
--记录锁定表
create table lock(departmentid int,dt datetime)
go
--因为函数中不可以用getdate,所以用个视图,得到当前时间
create view v_getdate as select dt=getdate()
go
--创建自定义函数,判断记录是否锁定
create function f_chk(@departmentid int)
returns bit
as
begin
declare @re bit,@dt datetime
select @dt=dt from v_getdate
if exists(select 1 from lock where departmentid=@departmentid
and datediff(minute,dt,@dt)<5) --锁的超时时间为5分钟
set @re=1
else
set @re=0
return(@re)
end
go
--数据处理测试,操作记录3
if dbo.f_chk(3)=1
print '记录被锁定'
else
begin
begin tran
insert into lock values(3,getdate())
update 部门 set name='A' where departmentid=3
delete from lock where departmentid=3
commit tran
end
--删除测试环境
drop table 部门
drop view v_getdate
drop function f_chkTop
3 楼unsigned(僵哥(发站内消息,请附上链接或问题说明,否则不予回复))回复于 2006-04-04 14:21:01 得分 0
1、需要行锁,如果使用Select Code,Content From Table1 with (ROWLOCK) Where Code = '001',这样操作是否正确?(想实现该行不能进行写操作,如果有写入的操作,就报错)
>>如此最好帶UDPLOCK,即更新鎖,則在你的事務完成之後該鎖定一直有效
2、如何解锁
>>鎖是相對事務而言的,所以正常情況下是當你的事務完成(包括提交或者回滾)了就解鎖了,若異常也可以使用Kill PID來解鎖,相關請查閱Kill命隻
3、如何读取出某一行正被哪个用户进行操作?
>>這個有點太複雜須查詢相關的系統表,曾經在一國外的站點上面有看到一個別人寫的存儲過程,但是系統重裝後沒有了^_^Top
4 楼unsigned(僵哥(发站内消息,请附上链接或问题说明,否则不予回复))回复于 2006-04-04 14:21:43 得分 0
>>如此最好帶UDPLOCK,即更新鎖,則在你的事務完成之後該鎖定一直有效
-------------
應該是事務完成之前一直有效.Top
5 楼jjkk168(老加班的人--好好学习,天天吃饭)回复于 2006-04-04 18:02:08 得分 0
不是太明白,再顶Top
6 楼unsigned(僵哥(发站内消息,请附上链接或问题说明,否则不予回复))回复于 2006-04-04 18:57:34 得分 0
connction.BegeinTrans
try
connection.Execute('Select Code,Content From Table1 with (ROWLOCK,UDPLOCK,HOLDLOCK) Where Code = ''001''');
...
connction.CommitTrans;
except
connection.RollBackTrans;
end;Top
7 楼unsigned(僵哥(发站内消息,请附上链接或问题说明,否则不予回复))回复于 2006-04-04 18:59:16 得分 0
倒,不小心把这看作Delphi了
connection.BeginTrans
on error goto errhandle
call connection.execute("Select Code,Content From Table1 with (ROWLOCK,UDPLOCK,HOLDLOCK) Where Code = '001'")
...
connection.CommitTrans
exit sub/function
errhandle:
connection.rollbacktrans
end sub/functionTop
8 楼jjkk168(老加班的人--好好学习,天天吃饭)回复于 2006-04-04 23:23:35 得分 0
谢谢!
那么需要解锁就只需要rollback trans就行了?
还有,能否帮我查出是哪个客户端正在锁定该行的代码?谢谢!Top
9 楼unsigned(僵哥(发站内消息,请附上链接或问题说明,否则不予回复))回复于 2006-04-04 23:57:10 得分 0
并不能知道是哪个客户端在锁定,除非你有登记各客户端连接在SQL当中相应的PID,最多只能查到哪个表的哪条记录被锁定Top
10 楼jjkk168(老加班的人--好好学习,天天吃饭)回复于 2006-04-05 11:04:03 得分 0
我可以做到登记PID,但如何查到这个表中的这一行记录被哪个PID锁定?谢谢Top
11 楼unsigned(僵哥(发站内消息,请附上链接或问题说明,否则不予回复))回复于 2006-04-05 13:30:42 得分 110
不好意思是SPID
當前連接的SPID可以使用:select @@SPID獲得
不好意思,剛查了一下資料.
對於鎖定的行,系統是資源來記錄,而並不記錄具體的行,所以無法知道鎖定到哪一行.
create procedure sp_lock2
@spid1 int = NULL, /* server process id to check for locks */
@spid2 int = NULL /* other process id to check for locks */
as
set nocount on
/*
** Show the locks for both parameters.
*/
declare @objid int,
@indid int,
@dbid int,
@string Nvarchar(255)
CREATE TABLE #locktable
(
spid smallint
,loginname nvarchar(20)
,hostname nvarchar(30)
,dbid int
,dbname nvarchar(20)
,ObjOwner nvarchar(128)
,objid int
,ObjName nvarchar(128)
,indid int
,IndName nvarchar(128)
,Type nvarchar(4)
,Resource nvarchar(16)
,Mode nvarchar(8)
,Status nvarchar(5)
)
if @spid1 is not NULL
begin
INSERT #locktable
(
spid
,loginname
,hostname
,dbid
,dbname
,ObjOwner
,objid
,ObjName
,indid
,IndName
,Type
,Resource
,Mode
,Status
)
select convert (smallint, l.req_spid)
,coalesce(substring (s.loginame, 1, 20),'')
,coalesce(substring (s.hostname, 1, 30),'')
,l.rsc_dbid
,substring (db_name(l.rsc_dbid), 1, 20)
,''
,l.rsc_objid
,''
,l.rsc_indid
,''
,substring (v.name, 1, 4)
,substring (l.rsc_text, 1, 16)
,substring (u.name, 1, 8)
,substring (x.name, 1, 5)
from master.dbo.syslockinfo l,
master.dbo.spt_values v,
master.dbo.spt_values x,
master.dbo.spt_values u,
master.dbo.sysprocesses s
where l.rsc_type = v.number
and v.type = 'LR'
and l.req_status = x.number
and x.type = 'LS'
and l.req_mode + 1 = u.number
and u.type = 'L'
and req_spid in (@spid1, @spid2)
and req_spid = s.spid
end
/*
** No parameters, so show all the locks.
*/
else
begin
INSERT #locktable
(
spid
,loginname
,hostname
,dbid
,dbname
,ObjOwner
,objid
,ObjName
,indid
,IndName
,Type
,Resource
,Mode
,Status
)
select convert (smallint, l.req_spid)
,coalesce(substring (s.loginame, 1, 20),'')
,coalesce(substring (s.hostname, 1, 30),'')
,l.rsc_dbid
,substring (db_name(l.rsc_dbid), 1, 20)
,''
,l.rsc_objid
,''
,l.rsc_indid
,''
,substring (v.name, 1, 4)
,substring (l.rsc_text, 1, 16)
,substring (u.name, 1, 8)
,substring (x.name, 1, 5)
from master.dbo.syslockinfo l,
master.dbo.spt_values v,
master.dbo.spt_values x,
master.dbo.spt_values u,
master.dbo.sysprocesses s
where l.rsc_type = v.number
and v.type = 'LR'
and l.req_status = x.number
and x.type = 'LS'
and l.req_mode + 1 = u.number
and u.type = 'L'
and req_spid = s.spid
order by spid
END
DECLARE lock_cursor CURSOR
FOR SELECT dbid, objid, indid FROM #locktable
WHERE Type <>'DB' and Type <> 'FIL'
OPEN lock_cursor
FETCH NEXT FROM lock_cursor INTO @dbid, @objid, @indid
WHILE @@FETCH_STATUS = 0
BEGIN
SELECT @string =
'USE ' + db_name(@dbid) + char(13)
+ 'update #locktable set ObjName = name, ObjOwner = USER_NAME(uid)'
+ ' from sysobjects where id = ' + convert(varchar(32),@objid)
+ ' and objid = ' + convert(varchar(32),@objid)
+ ' and dbid = ' + convert(varchar(32),@dbid)
EXECUTE (@string)
SELECT @string =
'USE ' + db_name(@dbid) + char(13)
+ 'update #locktable set IndName = i.name from sysindexes i '
+ ' where i.id = ' + convert(varchar(32),@objid)
+ ' and i.indid = ' + convert(varchar(32),@indid)
+ ' and objid = ' + convert(varchar(32),@objid)
+ ' and dbid = ' + convert(varchar(32),@dbid)
+ ' and #locktable.indid = ' + convert(varchar(32),@indid)
EXECUTE (@string)
FETCH NEXT FROM lock_cursor INTO @dbid, @objid, @indid
END
CLOSE lock_cursor
DEALLOCATE lock_cursor
SELECT * FROM #locktable
--return (0)
-- END sp_lock2
Top




