面试题:考虑并发情况下,轮流指定病人取药的药房窗口的存储过程。
写一个类似发放扑克牌的存储过程,医院药房有6个窗口,病人交费的时候,
轮流指定取药窗口。要考虑并发问题和锁的问题。
表结构如下:
DrugRoomWindow(--表:药房窗口表,用以记录窗口开放状态和被选中状态
WindowID int ,--药房窗口号
WindowName nvarchar(20),--药房窗口名称
,IsActive bit ,--是否开放(高峰期6个窗口都开放,但其它时候可能只开放部分窗口)
IsSelected bit--选中状态
)
=============================================================
我写的存储过程如下,请问对表进行加锁,这样操作对吗?谢谢!
=============================================================
create procedure uspGetNextWindow
as
select 1 from DrugRoomWindow with(TABLOCKX) --加锁使其它进程不能对表DrugRoomWindow进行读和写
set @CurrentWindowID=(select top 1 WindowID from DrugRoomWindow where IsSelected=1)--当前排到哪个窗口
if @CurrentWindowID is null
set @CurrentWindowID=1 --解决DrugRoomWindow初使状态所用记录的IsSelected=0的情况
set @NextWindowID=(select top 1 WindowID from DrugRoomWindow where WindowID>@CurrentWindowID and IsActive=1
and IsSelected<>1 order by WindowID)
if @NextWindowID is null
begin
--print '没有比它大的窗口,则取比当前窗口号小的最小窗口号'
set @NextWindowID=(select top 1 WindowID from DrugRoomWindow
where WindowID<@CurrentWindowID and IsActive=1 and IsSelected<>1 order by WindowID)
end
end
----------2、更新-----
set xact_abort on
begin trans
update DrugRoomWindow set IsSelected=1 where WindowID=@NextWindowID
update DrugRoomWindow set IsSelected=0 where WindowID<>@NextWindowID
commit
----------3、输出结果
select * from DrugRoomWindow where WindowID=@NextWindowID--输出
[解决办法]
上面你已经使用了事务处理,再加上异常处理就可以了
begin try
end try
begin catch
end catch
[解决办法]
--DROP TABLE DrugRoomWindow;
create table DrugRoomWindow(--表:药房窗口表,用以记录窗口开放状态和被选中状态
WindowID int ,--药房窗口号
WindowName nvarchar(20),--药房窗口名称
IsActive bit ,--是否开放(高峰期6个窗口都开放,但其它时候可能只开放部分窗口)
IsSelected BIT,--选中状态
OrderNum INT, /*优选顺序*/
UseCount INT NOT NULL DEFAULT 0
)
GO
INSERT INTO [dbo].[DrugRoomWindow] ([WindowID],[WindowName]
,[IsActive],[IsSelected],[OrderNum])
SELECT 10,'窗口10',1,0,10 union all
SELECT 20,'窗口20',1,0,20 union all
SELECT 30,'窗口30',1,0,30 union all
SELECT 40,'窗口40',1,0,40 union all
SELECT 50,'窗口50.此窗口特殊。排在最后被使用',1,0,60 union all
SELECT 60,'窗口60',1,0,50
--drop proc uspGetNextWindow;
GO
CREATE PROC uspGetNextWindow
AS
CREATE TABLE #tb(deWindowID INT)
UPDATE [DrugRoomWindow] SET IsSelected = 1,UseCount = UseCount+1
OUTPUT DELETED.[WindowID] INTO #tb WHERE WindowID IN(
SELECT TOP 1 drw.WindowID FROM DrugRoomWindow drw
WHERE drw.IsActive=1 /*开放的*/ and drw.IsSelected=0 /*未选中*/
ORDER BY drw.UseCount,drw.OrderNum
)
/*下一个可用的窗口。(没有结果集,则全部的窗口正忙,或全部的窗口被关闭)*/
SELECT * FROM [DrugRoomWindow] dw
WHERE dw.WindowID=(SELECT deWindowID FROM #tb)
GO
EXEC uspGetNextWindow
--楼主看一下我的是不是符合你的需求呢。我加了2上字段。
--如果关闭某些窗口时,请将UserCount字段值改为 开放窗口里最小的UserCount的值