Есть ли способ проверить, удалится ли DELETE из-за ограничений?

10

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

Какие у меня есть варианты для этого? Есть ли простой способ выполнить «пробный запуск» УДАЛЕНИЯ?

Джей Салливан
источник
Вы пытаетесь предотвратить исключение только для этого оператора, или вы пытаетесь облегчить обработку ошибок в более крупном пакете, который содержит это удаление?
Аарон Бертран
3
Не могли бы вы проверить, существует ли FK, и запустить оператор SELECT, чтобы проверить значения?
SQLRockstar,
Аарон: Нам нужно запустить пакет из нескольких DELETE в отдельных транзакциях. Если один терпит неудачу, другие уже совершены. (Плохой дизайн с самого начала, я знаю, но это не мое приложение, и оно не меняется.) Лучший обходной путь на данный момент звучит как сухая проверка, чтобы убедиться, что DELETE не удастся.
Джей Салливан
Все еще не уверен, что понимаю. Вы пытаетесь позволить удаляться остальным удаляемым файлам, или вы пытаетесь заранее проверить, все ли удаления будут успешными, или ни одно из них не должно?
Аарон Бертран
Аарон: Извините, я не дал понять, но да, я пытаюсь убедиться, что все они преуспевают, или ни один из них не преуспевает.
Джей Салливан

Ответы:

24

Если вашей целью является обработка всех удалений, только если все они выполнены успешно, почему бы просто не использовать TRY / CATCH:

BEGIN TRANSACTION;
BEGIN TRY
  DELETE #1;
  DELETE #2;
  DELETE #3;
  COMMIT TRANSACTION;
END TRY
BEGIN CATCH
  ROLLBACK TRANSACTION;
END CATCH

Если цель состоит в том, чтобы все успешные удаления были успешными, даже если один или несколько не удастся выполнить, вы можете использовать отдельные TRY / CATCH, например

BEGIN TRY
  DELETE #1;
END TRY
BEGIN CATCH
  PRINT 1;
END CATCH

BEGIN TRY
  DELETE #2;
END TRY
BEGIN CATCH
  PRINT 1;
END CATCH
Аарон Бертран
источник
6

Один из вариантов - начать транзакцию, запустить удаление, а затем всегда выполнять откат:

begin tran

delete Table1 where col1 = 1

-- Test whether it is there
select * from Table1 where col1 = 1

rollback tran

-- Confirm that it is still there
select * from Table1 where col1 = 1
GaTechThomas
источник
1
И если удаление прошло успешно, запустите его снова? Что делать, если удаление очень дорого? И если удаление не удается, что тогда? Вы сделали одно удаление и два выбора. Как я решу перейти на следующее удаление или нет?
Аарон Бертран
1
Если они являются частью требований, то они должны быть обработаны. Этот ответ касается «простого« пробного прогона »».
GaTechThomas
Ну, они не были, когда вы впервые представили свой ответ, но теперь они были уточнены.
Аарон Бертран
4
@GaTechThomas Аарон вносит большой вклад, поэтому он иногда краток, но я уверен, что его намерение не было быть агрессивным. Я обсуждал это в Куче, и я был бы благодарен за возможность сделать это с вами.
Джек говорит, попробуйте topanswers.xyz
1
@JackDouglas Я прочитал комментарии кучи, на которые вы ссылаетесь и понимаете суть. Комментарии сообщества были разумными, за исключением той части, где меня называли клоуном за указание на его агрессию. Я не понимаю, как я тот, кого считали агрессивным. Я разместил законный ответ на вопрос, который был задан в то время. При этом не просят о качестве продукции - иногда нужно просто быстро и просто. Так что на мой ответ я получаю острые вопросы. Похоже, он клеветал на мой ответ, чтобы его выбрали. Должны ли мы взять эту тему в другом месте?
GaTechThomas
0

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

Этот выберет записи из таблицы и затем попытается удалить их без исключений:

DECLARE @MaxErrors INT
SET @MaxErrors = 5;    // Setting 0 will stop process after the first error!

SELECT
    [Id]
    , ROW_NUMBER() OVER (ORDER BY Id ASC) AS [Index]
INTO #DeletingItems
FROM myTable

DECLARE @Current INT, @Max INT, @Id INT, @TotErrors INT
SELECT
    @Current = 1
    , @TotErrors = 0
    , @Max = MAX([Index])
FROM #DeletingTable

WHILE @Current <= @Max
BEGIN
    SELECT
        @Id = [Id]
    FROM #DeletingItems
    WHERE
        [Index] = @Index;

    BEGIN TRANSACTION;    
    BEGIN TRY    
        DELETE FROM myTable WHERE [Id] = @Id;

        COMMIT TRANSACTION;    
    END TRY    
    BEGIN CATCH    
        ROLLBACK TRANSACTION;

        SET @TotErrors = @TotErrors + 1;

        IF @TotErrors > @MaxErrors
            BREAK;
    END CATCH

    SET @Current = @Current + 1;
END
Maxe
источник
1
Почему? Как это улучшение по сравнению с принятым ответом?
ToolmakerSteve