光标存储过程是permacycling

时间:2011-10-05 12:53:06

标签: sql-server tsql stored-procedures cursor

这是perma-looping

的任何原因的存储过程

我必须做一些声明

这是我第一次使用SCROLL KEYSET SCROLL_LOCKSWHERE 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
...                 | ...                 | ...

如何计算[num_subproceso]

永久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] ...

的每个值重复相同的操作

SP代码

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;

这里有什么问题?

1 个答案:

答案 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