Вопрос по ссылочной целостности.

asvMan
Дата: 16.01.2009 18:14:00
Всем Добрый вечер.

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

1. Тригеры
2. ON DELETE CASCADE
3. Хранимая процедура, внутри нее в рамках транзакции выполняем запросы на удаление.

При этом нужно быть на 100% уверенным, что все отработало по принципу "все или ничего".
Если сделать через хранимку, то тут пноятно. А что по поводу путей 1 и 2? Как-то можно сделать так, что все каскадные удаления были в рамках одной транзакции? Или все же лучше использовать хранимые процедуры для такого?

С уважением,
Алексей.
iap
Дата: 16.01.2009 19:22:41
IMHO, если бы сервер мог удалить из таблицы запись и оставить в другой таблице запись, ссылающуюся на удалённую, то это означало бы полное отсутствие поддержки ссылочной целостности, несмотря на существующие ограничения FOREIGN KEY.
Разве такое можно себе представить?!
asvMan
Дата: 16.01.2009 19:52:21
iap,

Приведу пример. Есть 3 таблицы, А, B, C. C ссылается на B, B на А.
Теперь, в А удаляем запись, соответственно, удаляются в B, а затем и в C. Что произойдет, если где-то в середине этой цепочки возникнет ошибка? Изменения откатятся во всех таблицах?

Честно скажу, не пробовал решать вопрос через констрейнты. При использовании тригеров, при возникновении ошибки где-либо в цепи, все изменения не откатываются, хотя судя по хэлпу то должны.
alexeyvg
Дата: 16.01.2009 20:12:25
asvMan
Приведу пример. Есть 3 таблицы, А, B, C. C ссылается на B, B на А.
Теперь, в А удаляем запись, соответственно, удаляются в B, а затем и в C. Что произойдет, если где-то в середине этой цепочки возникнет ошибка? Изменения откатятся во всех таблицах?
Нет, но это не нарушает целостности. Она нарушится, если мы удалим записи в А, а в B и C останутся. Вот для исключения этой ситуации и нужны констрейны. И эта ситуация для системы потенциально более опасна, чем первая.

А вам надо ещё про транзакции почитать.
asvMan
Дата: 16.01.2009 20:25:09
alexeyvg
asvMan
Приведу пример. Есть 3 таблицы, А, B, C. C ссылается на B, B на А.
Теперь, в А удаляем запись, соответственно, удаляются в B, а затем и в C. Что произойдет, если где-то в середине этой цепочки возникнет ошибка? Изменения откатятся во всех таблицах?
Нет, но это не нарушает целостности. Она нарушится, если мы удалим записи в А, а в B и C останутся. Вот для исключения этой ситуации и нужны констрейны. И эта ситуация для системы потенциально более опасна, чем первая.

А вам надо ещё про транзакции почитать.


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

Пытался достигнуть такого результата с помощью тригеров, но не получается. Если 2 тригера отработали успешно, а третий нейдачно, то изменения первых двух не откатываются, а мне надо откатить. Как это реализовать?
iap
Дата: 16.01.2009 21:10:05
alexeyvg
asvMan
Приведу пример. Есть 3 таблицы, А, B, C. C ссылается на B, B на А.
Теперь, в А удаляем запись, соответственно, удаляются в B, а затем и в C. Что произойдет, если где-то в середине этой цепочки возникнет ошибка? Изменения откатятся во всех таблицах?
Нет, но это не нарушает целостности. Она нарушится, если мы удалим записи в А, а в B и C останутся.
Разве эти Ваши фразы не противоречат друг другу?

Ребята, а что нам мешает проделать маленький опыт?
SET NOCOUNT ON;
USE tempdb;
IF OBJECT_ID(N'F2','U') IS NOT NULL DROP TABLE F2;
IF OBJECT_ID(N'F1','U') IS NOT NULL DROP TABLE F1;
IF OBJECT_ID(N'T','U') IS NOT NULL DROP TABLE T;
GO
CREATE TABLE T(ID INT NOT NULL IDENTITY CONSTRAINT pkT PRIMARY KEY(ID));
GO
CREATE TABLE F1
(
 ID INT NOT NULL IDENTITY CONSTRAINT pkF1 PRIMARY KEY(ID),
 IDT INT CONSTRAINT fkF1T FOREIGN KEY(IDT) REFERENCES T(ID) ON DELETE CASCADE);
GO
CREATE TABLE F2
(
 ID INT NOT NULL IDENTITY CONSTRAINT pkF2 PRIMARY KEY(ID),
 IDF1 INT CONSTRAINT fkF2F1 FOREIGN KEY(IDF1) REFERENCES F1(ID) ON DELETE CASCADE);
GO
CREATE TRIGGER tdF2 ON F2 FOR DELETE AS RAISERROR('Опаньки!',16,0);
GO
INSERT T DEFAULT VALUES;
INSERT T DEFAULT VALUES;
GO
INSERT F1(IDT) SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 2;
GO
INSERT F2(IDF1) SELECT 1 UNION ALL SELECT 3;
GO
/*
DISABLE TRIGGER ALL ON F2;
ENABLE TRIGGER ALL ON F2;
SET XACT_ABORT OFF;
*/
DELETE T WHERE ID=1;
GO
SELECT * FROM T;
SELECT * FROM F1;
SELECT * FROM F2;
GO
IF OBJECT_ID(N'F2','U') IS NOT NULL DROP TABLE F2;
IF OBJECT_ID(N'F1','U') IS NOT NULL DROP TABLE F1;
IF OBJECT_ID(N'T','U') IS NOT NULL DROP TABLE T;
GO
Можно ещё поиграться с содержимым комментария.
Но REFERENSIAL INTEGRITY всё равно не может нарушаться при живых FOREIGN KEYs!
Glory
Дата: 17.01.2009 00:48:10
asvMan

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

А транзакцию то вы использовали ?
asvMan
Дата: 17.01.2009 19:47:04
Подскажите, пожалуйста, как сделать так, чтобы вся цепочка вызовов тригеров выполнялась в одной транзакции.

Спасибо.
iap
Дата: 17.01.2009 20:28:06
asvMan,

во-первых, непонятно, чем не устраивают каскадные констрейнты
во-вторых, почему бы удаление из подчинённых таблиц не организовывать из триггера
на удаление из основной таблицы? Триггер всегда выполняется в неявной транзакции.
Glory
Дата: 17.01.2009 20:37:56
asvMan
Подскажите, пожалуйста, как сделать так, чтобы вся цепочка вызовов тригеров выполнялась в одной транзакции.

Спасибо.

Использовать команду BEGIN TRANSACTION