Каков правильный порядок вставки / удаления / изменения в наборе данных? - PullRequest
16 голосов
/ 21 марта 2012

MSDN утверждает, что заказ :

  1. Дочерняя таблица: удалить записи.
  2. Родительская таблица: вставка, обновление и удаление записей.
  3. Дочерняя таблица: вставка и обновление записей.

У меня проблема с этим.

Пример: ParentTable имеет две записи parent1 (Id: 1) и parent2 (Id: 2)

ChildTable имеет запись child1 (Id: 1, ParentId: 1)

Если мы обновим child1, чтобы иметь нового parent parent2, а затем удалим parent1.

  1. Нам нечего удалять в дочерней таблице
  2. Мы удаляем parent1: мы нарушили ограничение, потому что дочерний элемент все еще привязан к parent1, если мы не обновим его сначала.

Так, каков правильный порядок, и является ли MSDN ложным по теме?

Мои мысли о персонале

  1. Дочерняя таблица: удалить записи.
  2. Родительская таблица: вставить, обновить записи.
  3. Дочерняя таблица: вставка и обновление записей.
  4. Родительская таблица: удалить записи.

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

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

Ответы [ 6 ]

4 голосов
/ 28 марта 2012

Не поддерживает ли ваш продукт SQL отложенную проверку ограничений?

Если нет, вы можете попробовать

Удалить все дочерние записи - удалить все родительские записи - вставить все родительские записи - вставить все дочерние записи

, где любые ОБНОВЛЕНИЯ были разделены на составляющие их УДАЛЕНИЯ и ВСТАВКИ.

Это должно работать правильно во всех случаях, но на приемлемых скоростях, вероятно, ни в одном ...

Также доказано, что это единственная схема, которая может работать корректно во всех случаях, поскольку:

(a) ключевые ограничения родительского элемента диктуют, что родительский DELETES должен предшествовать родительскому INSERTS,
(б) ключевые ограничения на дочерний элемент диктуют, что дочерние DELETES должны предшествовать дочерним INSERTS,
(c) FK диктует, что дочерние DELETES должны предшествовать родительским DELETES
(d) FK также диктует, что детские вкладыши должны следовать за родительскими вставками

Данная последовательность является единственно возможной, которая удовлетворяет этим 4 требованиям, и она также показывает, что ОБНОВЛЕНИЯ для ребенка делают решение невозможным, несмотря ни на что, поскольку ОБНОВЛЕНИЕ означает «одновременное» УДАЛЕНИЕ плюс ВСТАВКА.

4 голосов
/ 23 марта 2012

Вы должны принять во внимание их контекст.MS сказала

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

в контекстенаписания прикладного программного обеспечения для клиентских данных.

Почему так важно снизить вероятность нарушения ограничений ссылочной целостности?Поскольку нарушение этих ограничений означает

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

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

  • Если обновление является операцией DELETE для ссылочной таблицы, и если внешние ключи в ссылочных таблицах объявлены как ON DELETE CASCADE, то оптимальным вариантом будет просто удалить указанную ссылкуrow (родительская строка), и пусть dbms управляет каскадом.(Это также оптимальная вещь для ON DELETE SET DEFAULT и для ON DELETE SET NULL.)

  • Если обновление является операцией DELETE в указанной таблице, и если внешние ключи вссылочные таблицы объявляются как ON DELETE RESTRICT, тогда оптимально сначала удалить все ссылочные строки (дочерние строки), а затем удалить ссылочную строку.

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

Вы сказали

Пример: ParentTable имеет две записиparent1 (Id: 1) и parent2 (Id: 2)

ChildTable имеет запись child1 (Id: 1, ParentId: 1)

Если мы обновим child1, чтобы иметь нового parent2и мы удаляем parent1.

  1. Нам нечего удалять в дочерней таблице
  2. Мы удаляем parent1: мы нарушили ограничение, потому что дочерний элемент все еще привязан к parent1, если только мы несначала обновите его.

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

  1. Обновите дочерний элемент, чтобы иметь нового родителя, затем подтвердите.Эти данные должны быть исправлены независимо от того, что происходит с первым родителем.В частности, эти данные должны быть исправлены, даже если есть параллельные обновления или другие ограничения, которые делают временно или навсегда невозможным удаление первого родителя.(Это не проблема ссылочной целостности, потому что в ограничениях внешнего ключа SQL нет предложения ON DELETE SET NEXT PARENT ID или MAKE YOUR BEST GUESS.)

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

3 голосов
/ 21 марта 2012

Звучит как:

  1. Вставить parent2.Child по-прежнему указывает на parent1.
  2. Обновите child, чтобы он указывал на parent2.Теперь ничто не ссылается на parent1.
  3. Удалить parent1.

Вы можете заключить его в транзакцию, где это возможно.

В зависимости от вашей схемы, вы также можетерасширите это до:

  1. Обновите parent1, чтобы указать, что он заблокирован (или заблокируйте его в БД), тем самым предотвращая обновления.
  2. Вставьте parent2
  3. Обновите дочерний элементуказать на parent2
  4. Удалить parent1

Преимущество этого порядка состоит в том, что объединение между родителем и дочерним элементом будет возвращать согласованный результат во всем.Когда дочерний процесс обновляет результаты объединения, он «переворачивается» в новое состояние.

РЕДАКТИРОВАТЬ:

Другой вариант - переместить родительские / дочерние ссылки вдругая таблица, например, «ссылки»;

CREATE TABLE links (
    link_id INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
    parent_id INT NOT NULL,
    child_id INT NOT NULL
);

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

UPDATE links
    SET parent_id = @new_parent_id
    WHERE parent_id = @old_parent_id
    AND child_id = @child_id;
1 голос
/ 21 марта 2012

Необходимость УДАЛИТЬ родительскую запись без удаления дочерних записей достаточно необычна, так что я уверен, что обычно предписанный порядок операций с наборами данных, определенный MS, в этом случае не применяется.

Самый эффективный метод - ОБНОВИТЬ дочерние записи, чтобы отразить нового родителя, затем УДАЛИТЬ первоначального родителя. Как уже упоминалось, эту операцию следует выполнять в рамках транзакции.

0 голосов
/ 27 марта 2012

Заявление MSDN является верным на основании использования зависимостей (внешних ключей).Думайте о порядке как

  1. Дочерняя таблица (каскадное удаление)
  2. Родительская таблица: вставьте и / или обновите и / или удалите запись, означающую последний шаг каскадного удаления.
  3. Дочерняя таблица: вставить или обновить.

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

С другой стороны, вы можете подходить к вашему делу по-разному.Я думаю, что реальный (почти) сценарий будет более полезным.Предположим, что родительская таблица является основной частью заказов (orderID, clientID и т. Д.), А дочерняя таблица является частью сведений (detailID, orderID, productOrServiceID и т. Д.).Таким образом, вы получаете заказ, и у вас есть следующий

родительский стол

orderID = 1 (auto increment)
...

дочерний стол

detailID = 1 (auto increment)
orderID = 1
productOrServiceID = 342

and

detailID = 2
orderID = 1
productOrServiceID = 169

and

detailID = 3
orderID = 1
productOrServiceID = 307

Таким образом, у нас есть один заказ на три продукта / услуги.Теперь ваш клиент хочет, чтобы вы перевели второй продукт или услугу в новый заказ и доставили его позже.У вас есть два варианта сделать это.

Первый (прямой)

  • Создать новый заказ (новую родительскую запись), который получает orderID = 2

  • Обновите дочернюю таблицу, установив orderID = 2, где orderID = 1 и productOrServiceID = 169

В результате у вас будет

Родительская таблица

orderID = 1 (auto increment)
...

and

orderID = 2
...

Дочерний стол

detailID = 1 (auto increment)
orderID = 1
productOrServiceID = 342

and

detailID = 2
orderID = 2
productOrServiceID = 169

and

detailID = 3
orderID = 1
productOrServiceID = 307

Второй (косвенный)

  • Хранить DataRow второго продукта / услуги из дочерней таблицыв качестве переменной

  • Удалить относительную строку из дочерней таблицы

  • Создать новый заказ (новую родительскую запись), который получает orderID = 2

  • Вставьте сохраненный DataRow в дочернюю таблицу, изменив поле orderID с 1 на 2

В результате у вас будет

Родительская таблица

orderID = 1 (auto increment)
...

and

orderID = 2
...

Дочерняя таблица

detailID = 1 (auto increment)
orderID = 1
productOrServiceID = 342

and

detailID = 3
orderID = 1
productOrServiceID = 307

and

detailID = 4
orderID = 2
productOrServiceID = 169

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

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

0 голосов
/ 21 марта 2012

Я думаю, что разделение действий над таблицами не является хорошим дизайном, поэтому мое решение

  1. вставить / обновить / удалить родительскую таблицу
  2. вставить / обновить / удалить дочернюю таблицу

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

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