Лучший способ заблокировать базу данных SQL Server - PullRequest
0 голосов
/ 19 сентября 2019

Я пишу приложение на C ++, которое подключается к базе данных SQL Server через ODBC.

Мне нужна функция Archive, поэтому я собираюсь написать хранимую процедуру, которая принимает дату.Он будет суммировать все транзакции и платежи до этой даты для каждого клиента, соответственно обновлять начальный баланс клиента, а затем удалять все транзакции и платежи до этой даты.

Мне кажется, что это может бытьочень плохо, если кто-то еще добавляет или удаляет транзакции или платежи одновременно, эта хранимая процедура выполняется.Поэтому я думаю, что мне следует заблокировать всю базу данных во время выполнения, что случается не так часто.

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

ОБНОВЛЕНИЕ:

Основываясь на ответе user12069178, вот что я придумал до сих пор.Буду признателен за любые отзывы об этом.

ALTER PROCEDURE [dbo].[ArchiveData] @ArchiveDateTime DATETIME
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    DECLARE @TempTable TABLE
    (
        CustomerId INT,
        Amount BIGINT
    );

    BEGIN TRANSACTION;

    -- Archive transactions
    DELETE Transactions WITH (TABLOCK)
    OUTPUT deleted.CustomerId, deleted.TotalAmount INTO @TempTable
    WHERE [TimeStamp] < @ArchiveDateTime;

    IF EXISTS (SELECT 1 FROM @TempTable)
    BEGIN
        UPDATE Customers SET StartingBalance = StartingBalance +
        (SELECT SUM(Amount) FROM @TempTable temp WHERE Id = temp.CustomerId)
    END;

    DELETE FROM @TempTable

    -- Archive payments
    DELETE Payments WITH (TABLOCK)
    OUTPUT deleted.CustomerId, deleted.Amount INTO @TempTable
    WHERE [Date] < @ArchiveDateTime;

    IF EXISTS (SELECT 1 FROM @TempTable)
    BEGIN
        UPDATE Customers SET StartingBalance = StartingBalance -
        (SELECT SUM(Amount) FROM @TempTable temp WHERE Id = temp.CustomerId)
    END;

    COMMIT TRANSACTION;
END

1 Ответ

1 голос
/ 19 сентября 2019

Как правило, чтобы убедиться, что удаляемые строки - это те, которые вы суммируете и вставляете, - используйте предложение OUTPUT при удалении.Он может выводить строки, которые были выбраны для удаления.

Вот установка, которая даст нам некоторые транзакции:

USE tempdb;
GO

DROP TABLE IF EXISTS dbo.Transactions;
GO

CREATE TABLE dbo.Transactions
(
    TransactionID int NOT NULL IDENTITY(1,1)
        CONSTRAINT PK_dbo_Transactions
        PRIMARY KEY,
    TransactionAmount decimal(18,2) NOT NULL,
    TransactionDate date NOT NULL
);
GO

SET NOCOUNT ON;

DECLARE @Counter int = 1;
WHILE @Counter <= 50
BEGIN
    INSERT dbo.Transactions 
    (
        TransactionAmount, TransactionDate
    )
    VALUES (ABS(CHECKSUM(NewId())) % 10 + 1, DATEADD(day, 0 - @Counter * 3, GETDATE()));

    SET @Counter += 1;
END;

SELECT * FROM dbo.Transactions;
GO

Теперь следующий код удаляет строки после обрезки и одновременно выводит суммы в переменную таблицы, а затем вставляетобщая строка в таблице транзакций.

DECLARE @CutoffDate date = DATEADD(day, 1, EOMONTH(DATEADD(month, -2, GETDATE())));
DECLARE @TransactionAmounts TABLE
(
    TransactionAmount decimal(18,2)
);

BEGIN TRAN;

DELETE dbo.Transactions WITH (TABLOCK)
OUTPUT deleted.TransactionAmount INTO @TransactionAmounts 
WHERE TransactionDate < @CutoffDate;

IF EXISTS (SELECT 1 FROM @TransactionAmounts)
BEGIN
    INSERT dbo.Transactions (TransactionAmount, TransactionDate)
    SELECT SUM(TransactionAmount), DATEADD(day, 1, @CutoffDate)
    FROM @TransactionAmounts;
END;

COMMIT;

Обычно я стараюсь по возможности избегать указания блокировок, но, основываясь на вашем предложении, я их добавил.Если бы у вас не было блокировки таблицы, все равно было бы хорошо, но это означало бы, что даже если кто-то добавит новую «старую» строку, пока вы это делаете, она не будет в общем количестве или удалена.Создание сериализуемой транзакции также приведет к результату и будет блокировать меньше, чем блокировка таблицы, если число удаляемых строк было меньше порога эскалации блокировки (по умолчанию 5000).

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...