Хранимая процедура с курсором является пермациклированием - PullRequest
0 голосов
/ 05 октября 2011

Это хранимая процедура по любой причине, эта циклическая перма

Я должен сделать несколько ударов

, когда я впервые использую SCROLL KEYSET SCROLL_LOCKS и WHERE CURRENT OF

краткое объяснение того, что это делает: замена временного идентификатора для многих строк постоянным идентификатором, который является MAX(num_subproceso)+1, но я хочу, чтобы это делалось в ACID-режиме для устранения возможных совпадений.

IЯ не могу использовать транзакцию непосредственно в моем приложении, причина в том, что я использую набор классов моего авторства, которые абстрагируют структуру базы данных и поведение данных таким образом, только недостатком моей конкретной реализации являетсячто я не могу использовать транзакцию, потому что мой код часто в циклах быстрого открытия / закрытия соединения, и мне нужно сделать транзакцию из большого количества sqls.

Чтобы избежать этого, я закончил генерировать отрицательный случайный идентификатор для записи строк в базу данных, а затем использовал сохраненный процесс для использования транзакции и использования курсора с блокировкой строки, чтобы избежать (может быть, я ошибаюсь здесь)этот другой пользователь получает тот же идентификатор, используя MAX (...) + 1, и возникает проблема, когда 2 пользователя пишут один и тот же первичный ключ.

Почему я говорю, что предотвращаю проблему параллелизма (я могошибаться): если я блокирую строку, которую я использую, а другой пользователь выполняет операцию (например, MAX (...), чтобы получить новый идентификатор) над всем набором строк в той же таблице, блокировка, котораяблокирование моей строки предотвращает получение 2-м клиентом результата и остается в состоянии ожидания до тех пор, пока не будет снята моя блокировка

Процесс длинный и выполняет различные шаги таким образом, чтобы проходить время между началом и завершением всего процесса.

... постоянный идентификатор - это максимальное число идентификаторов этого типа плюс один ... таким образом;в то время как временный идентификатор вначале одинаков, постоянный идентификатор может варьироваться

пример данных

(до)

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]

номер постоянного идентификатора основан на количестве [cve_subproceso] в таблице.

, относящихся к этому примеру ... существует или существовало взаданное время 156 'A01' изменяется от 1 до 156, когда я добавляю 157 к A01 в этом случае, потому что я вычислил 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 Ответ

1 голос
/ 05 октября 2011

Я думаю, что это должно делать то, что вам нужно.Вам нужно 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
...