Как попытаться удалить записи без завершения при ошибке

#sql #sql-server #tsql #sql-server-2008-r2

#sql #sql-сервер #tsql #sql-server-2008-r2

Вопрос:

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

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

Как я могу написать скрипт, который пытается удалить каждую запись в таблице и не завершает выполнение инструкции при первой ошибке?

С уважением,

непристойный

Редактировать:

Причина, по которой я хотел бы сделать что-то подобное, заключается в том, что бизнес-пользователи добавили много повторяющихся данных (т.Е.: Найдите кого-то и нажмите «Добавить как новый» вместо «Выбрать»). Теперь у нас может быть 10 человек, которые имеют только имя и не имеют отношения к другим таблицам. Я надеюсь, что это устраняет любую путаницу.

Комментарии:

1. Можете ли вы немного прояснить вопрос. (1) Похоже, у вас есть T_Parent, который имеет T_ChildCascade, и T_ChildNoCascade1, и T_ChildNoCascade2. (2) Ваш обычный вариант использования для удаления всех или одной записи? (3) Какое отношение к вопросу имеет набор данных? (4) Какая версия SQL Server?

2. Отличные вопросы: 1) Ваши предположения верны, на T_Parent ссылаются несколько таблиц T_Child. Один из них — T_ChildCascade, а многие — T_ChildNoCascade.

3. Прохладный. Как насчет других вопросов, которые я задавал?

4. 2) Обычным случаем удаления записи в приложении является удаление каждой ссылки отдельно. Я не мог выполнить каскадное удаление во многих таблицах из-за кругового каскадного дерева. Ситуация, которую я описываю, является просто тестовой ситуацией для сценария, который я писал, чтобы быть абсолютно уверенным, что он удалит только одну запись. 3) Набор данных — это просто предварительно заполненная база данных, которую я буду использовать для тестирования. Извините за любую путаницу. 4) Номер версии 10.50.1600.1

Ответ №1:

Я поиграл с разными идеями. Вот самый простой способ. Однако это довольно дорого. Опять же, это попытка удалить неиспользуемые повторяющиеся данные. 1000 записей заняли 8 минут. Может кто-нибудь придумать более эффективный способ сделать это?

 DECLARE @DeletedID Int

DECLARE ItemsToDelete SCROLL CurSor For
SELECT ID FROM ParentTable

Open ItemsToDelete

FETCH NEXT FROM ItemsToDelete INTO @DeletedID
     While @@FETCH_STATUS = 0
BEGIN
    BEGIN TRY
        --ATTEMPT TO DELETE
        DELETE FROM ParentTable WHERE ID = @DeletedID;
    END TRY 

    BEGIN CATCH
        --DO NOTHING
    END CATCH

        --FETCH NEXT ROW
    FETCH NEXT FROM ItemsToDelete INTO @DeletedID
END


Close ItemsToDelete

Deallocate ItemsToDelete
 

Ответ №2:

Отнеситесь к этому с недоверием: на самом деле я не администратор базы данных и никогда не работал с SQL Server. Однако:

На самом деле здесь вы сталкиваетесь с двумя разными правилами:

  1. Ссылочные ограничения
  2. Правила, зависящие от бизнеса (я предполагаю), разрешенные для удаления.

Похоже, что при настройке ссылочных ограничений (в дочерних таблицах) они были созданы с помощью опции RESTRICT или NO ACTION . Это означает, что попытка удаления из родительской таблицы при наличии дочерних строк приведет к сбою. Но загвоздка в том, что вы хотите в определенной таблице разрешить удаления и распространять их (опция CASCADE ). Итак, только для тех таблиц, в которых должно быть предложено удаление, измените ссылочное ограничение на использование CASCADE . В противном случае предотвратите удаление с помощью (уже существующей) ошибки.

Что касается обработки возникающих «исключений» … вот несколько способов справиться с ними:

  1. Предсказать их. Напишите свое удаление таким образом, чтобы не удалять что-либо, если на ключ есть ссылка в наборе дочерних таблиц. Очевидно, что это невозможно поддерживать в долгосрочной перспективе или для таблицы с большим количеством ссылок.
  2. Перехватить исключение. Я сомневаюсь, что они могут быть обнаружены «в сценарии» (особенно при запуске одного файла типа .txt), поэтому вам, вероятно, придется хотя бы написать хранимую процедуру или запустить с языка более высокого уровня. Вторая проблема здесь заключается в том, что вы не можете «пропустить» ошибку и «продолжить» удаление (о котором я знаю) — SQL будет продолжать подводить вас каждый раз (… в том, что составляет случайную строку). Вам придется перебирать весь файл по строкам (или, возможно, по набору строк, динамически), чтобы обеспечить удаление записи. Для небольшого файла (относительно) это может сработать.
  3. Напишите (набор) динамических инструкций, которые используют таблицы информационной схемы и поисковые запросы, чтобы исключить идентификаторы, включенные в «не удаляемые» таблицы. Интересная концепция, но потенциально сложная / дорогая.

Ответ №3:

Ну, каждый оператор в SQL рассматривается как транзакция, поэтому, если вы попытаетесь удалить несколько записей и столкнетесь с ошибкой, учитывая правила целостности, каждое внесенное вами до сих пор изменение будет откатано. То, что вы могли бы сделать, это написать запрос, который сначала удалит данные из ссылочной таблицы (таблица, которая имеет значение из внешнего ключа (таблица T_child в вашем случае), а затем удалит данные из таблицы T_parent.

В общем: сначала проверьте таблицу T_child на наличие записей, которые вы хотите удалить, а затем удалите записи из таблицы T_parent, чтобы избежать сбоя транзакции.

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

(Поправьте меня, если я ошибаюсь)

Ответ №4:

Здесь есть несколько вариантов. В вашем вопросе есть некоторая двусмысленность, поэтому я хочу сначала повторить ваш вариант использования (и я отвечу на ваш вопрос так, как я его понимаю).

Пример использования

База данных состоит из нескольких таблиц T_Parent, T_Child1, T_Child2, T_Child3. Полный набор данных будет содержать записи во всех 4 таблицах. Учитывая бизнес-требования, мы часто получаем частичные данные, которые необходимо удалить позже. Например, T_parent и T_Child2 могут получать данные, но не TC1 и TC3.

Мне нужно иметь возможность проверять наличие частичных данных и, если они найдены, удалять все частичные данные (T_Parent и T_Child 2 в моем примере, но это могут быть TP и TC3 или другие комбинации).


@ribald — Правильно ли я понимаю?

  • Прокомментируйте мое понимание, и я напишу ответ. Если комментарии недостаточно длинные или понятные, просто отредактируйте свой вопрос.
  • вы сказали «каскад» в терминах удаления (что означает очень специфическую вещь в SQL server), но в вашем более позднем описании это звучит больше так, как будто вы хотите удалить все частичные данные.
  • «Каскадирование» — это то, что доступно, но на самом деле не то, что вы включаете и выключаете в зависимости от условий некоторых данных.
  • когда вы сказали «набор данных», вы не имели в виду ADO.NET Dataset, вы просто имели в виду тестовые данные.
  • Я предполагаю, что вы не ищете хороший способ тестирования, вы просто хотите убедиться, что у вас есть целостность данных.