这是perma-looping
的任何原因的存储过程我必须做一些声明
这是我第一次使用SCROLL KEYSET SCROLL_LOCKS
和WHERE CURRENT OF
对此的简短解释:
使用MAX(num_subproceso)+1
的永久ID替换许多行的temporal属ID,但我希望它以ACID方式执行以解决可能的并发iusses。
我不能直接在我的应用程序中使用事务,原因是我使用的是我的作者类集,它以这种方式抽象数据库的结构和数据行为,只是缺点我的具体实现是我不能使用交易,因为我的代码经常在快速打开/关闭连接周期,我需要进行大块sqls的交易。
为了避免这种情况,我完成了生成负随机id以在数据库中写入行,然后使用存储过程来使用事务并使用带行锁的游标来避免(可能我在这里错了)其他用户使用MAX(...)+ 1得到相同的id,并发生一个问题,即2个用户写同一个主键。
为什么我说我正在阻止并发问题(我可能是错的):如果我锁定我正在使用的行并且其他用户进行操作(例如MAX(...)来获取新id)在同一个表中的整个行集上,阻塞我的行的锁阻止第二个客户端可以获得结果并保持等待状态直到我的锁被释放
这个过程很长,并且在整个过程的开始和结束之间花费一段时间来完成各种步骤。
...永久id是这种类型的id加上一个的最大数量......这样;虽然临时id在起初是相同的,但永久id可以变化
(前)
cve_subproceso (PK) | num_subproceso (PK) | inidate (dd-mm-yyyy hh:MM:ss)
--------------------+---------------------+---------------------
A01 | -27813578125 | 01-05-2011 09:00:00
A02 | -27813578125 | 15-05-2011 10:00:00
A03 | -27813578125 | 16-05-2011 07:30:00
A07 | -27813578125 | 21-05-2011 09:15:00
A12 | -27813578125 | 30-05-2011 10:00:00
... | ... | ...
(后)
cve_subproceso (PK) | num_subproceso (PK) | inidate (dd-mm-yyyy hh:MM:ss)
--------------------+---------------------+---------------------
A01 | 157 | 01-05-2011 09:00:00
A02 | 122 | 15-05-2011 10:00:00
A03 | 15 | 16-05-2011 07:30:00
A07 | 90 | 21-05-2011 09:15:00
A12 | 140 | 30-05-2011 10:00:00
... | ... | ...
永久ID的数量取决于表中[cve_subproceso]的数量。
与此示例相关的...在给定时间存在或存在156'A01'从1到156时,当我在这种情况下向A01添加157时,是因为我计算了MAX([num_subproceso])和MAX( )返回156
cve_subproceso (PK) | num_subproceso (PK) | inidate (dd-mm-yyyy hh:MM:ss)
--------------------+---------------------+---------------------
A01 | 156 | 01-05-2011 09:00:00
A01 | 155 | 15-04-2011 10:00:00
A01 | 154 | 03-04-2011 07:30:00
A01 | 152 | 11-03-2011 09:15:00
A01 | 151 | 10-01-2011 10:00:00
... | ... | ...
我对[cve_subproceso] ...
的每个值重复相同的操作ALTER PROCEDURE [dbo].[ReprocesarEventos]
-- Add the parameters for the stored procedure here
@numSubproceso int = 0
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
BEGIN TRANSACTION;
-- Insert statements for procedure here
DECLARE @nuevoID integer;
DECLARE @cveSubProc varchar(4);
DECLARE cUpd CURSOR SCROLL KEYSET SCROLL_LOCKS
FOR SELECT [cve_subproceso] FROM [dbo].[calendario] WHERE [num_subproceso]= @numSubproceso
FOR UPDATE OF [num_subproceso];
OPEN cUpd;
FETCH NEXT FROM cUpd INTO @cveSubProc;
WHILE (@@FETCH_STATUS <> -1)
BEGIN
IF (@@FETCH_STATUS <> -2)
BEGIN
SELECT @nuevoID=(ISNULL(MAX([num_subproceso]),0)+1) FROM [dbo].[calendario] WHERE [cve_subproceso]=RTRIM(LTRIM(@cveSubProc));
IF(@nuevoID < 0)
BEGIN
SET @nuevoID = 1;
END;
UPDATE [dbo].[calendario] SET [num_subproceso]=@nuevoID WHERE CURRENT OF cUpd;
END;
FETCH NEXT FROM cUpd INTO @cveSubProc;
END;
CLOSE cUpd;
DEALLOCATE cUpd;
IF(@@ERROR <> 0)
BEGIN
-- Rollback the transaction
ROLLBACK TRANSACTION;
-- Raise an error and return
RAISERROR ('Oops! - something went wrong.', 16, 1);
RETURN;
END;
COMMIT TRANSACTION;
END;
这里有什么问题?
答案 0 :(得分:1)
我认为这应该做你需要的。您需要SERIALIABLE
锁定MAX
以上的范围。这会导致阻塞,但这就是重点!
CREATE TABLE [dbo].[calendario](
[cve_subproceso] [char](3) NULL,
[num_subproceso] [bigint] NULL
)
INSERT INTO [dbo].[calendario]
SELECT 'A01',-27813578125 UNION ALL
SELECT 'A02',-27813578125 UNION ALL
SELECT 'A03',-27813578125 UNION ALL
SELECT 'A07',-27813578125 UNION ALL
SELECT 'A12',-27813578125 UNION ALL
SELECT 'A01',156 UNION ALL /*Add a few rows of pre-existing data*/
SELECT 'A01',157 UNION ALL
SELECT 'A02',121
DECLARE @numSubproceso BIGINT = -27813578125
/*Run the query*/
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
WITH t1
AS (SELECT *,
MAX(num_subproceso) OVER (PARTITION BY cve_subproceso) AS
max_num_subproceso
FROM [dbo].[calendario]),
t2
AS (SELECT *,
CASE
WHEN max_num_subproceso < 0 THEN 0
ELSE max_num_subproceso
END + Row_number() OVER (PARTITION BY cve_subproceso ORDER BY
cve_subproceso) AS
new_num_subproceso
FROM t1
WHERE [num_subproceso] = @numSubproceso)
UPDATE t2
SET num_subproceso = new_num_subproceso
cve_subproceso num_subproceso
-------------- --------------------
A01 158
A02 122
A03 1
A07 1
A12 1
A01 156
A01 157
A02 121