Каскадни обновявания и изтривания

Често срещани заявки над база данни са операциите изтриване и промяна на съществяуващ запис. Какво се случва обаче, когато имаме външен ключ от една таблица към друга и стойността, към която някой сочи бъде изтрита.

Всичко зависи от ограничението, което е декларирано по време на създаването на външния ключ. От статията за ограничения знаете, че върху външния ключ съществуват следните ограничения:

  • ON DELETE SET NULL
  • ON DELETE RESTRICT
  • ON DELETE CASCADE

Аналогично, може да декларирате и ON UPDATE CASCADE.  В този случай говорим за каскадни операции. Ако например сменим id-то на един треньор в нашата база при първоначално създадените таблици, какво ще се случи?

UPDATE coaches
SET id = 22
WHERE id = 1;

Или пък се опитаме да го изтрием:

DELETE
FROM coaches
WHERE id = 1;

 

Сървърът ще ни даде следната грешка:

Cannot delete or update a parent row: a foreign key constraint fails.

Това е така, тъй като, когато сме правили външен ключ в таблицата sportgroups, ние не сме указали изрично с кое ограничение да бъде създаден този ключ, който да сочи към колона id от таблица coaches. В този случай се използва ограничението по подразбиране от тип RESTRICT. Именно и затова ни е  забранено да променяме или изтриваме id  на треньор, който се реферира. Нека да променим постановката така: ще премахнем външния ключ в таблицата sportgroups, ще направим нов външен ключ върху същата колона coach_id и ще използваме CASCADE. За да видим как се казва ключът отиваме в изгледа за тази таблица и виждаме, че явно по време на създаването не сме дали смислени имена на нито един от ключовете и затова са използвани наименования по подразбиране. Нашият ключ е вторият.

1

ALTER TABLE sportgroups
DROP FOREIGN KEY sportgroups_ibfk_2;
ALTER TABLE sportgroups
ADD CONSTRAINT FOREIGN KEY `coach_id_key` (coach_id) references coaches(id)
ON DELETE SET NULL ON UPDATE CASCADE;

С горната заявка, при изтриване на треньор от базата, навсякъде в sportgroups, където се среща неговото id ще се записва NULL. А когато се прави обновяване на id-та, то те каскадно ще се обновят. Но няма смисъл да правим ON DELETE CASCADE – в този случай биха се изтрили всички спортни групи при даден треньор, ако той бъде изтрит.

Ето какво имаме в момента в sportgroups:

2

Ще сменим изпълним следната заявка:

DELETE
FROM coaches
WHERE id = 1;

Отново проверяваме sportgroups:

3

Нека изпълним следната заявка:

UPDATE coaches
SET id = 22
WHERE id = 2;

И да видим какво се е случило в sportgroups:

4

Съвсем очаквано там, където id-то на треньора е било 2, е станало 22.

За да видите дали някой сочи към определена колона, може да изпозлвате следната заявка:

USE information_schema;
 
SELECT table_name
FROM referential_constraints
WHERE constraint_schema = 'database_name' AND
      referenced_table_name = 'parent_table' AND
      delete_rule = 'CASCADE';

Може да смените delete_rule с update_rule:

USE information_schema;
 
SELECT table_name
FROM referential_constraints
WHERE constraint_schema = 'school_sport_clubs' 
AND referenced_table_name = 'coaches'
AND  update_rule = 'CASCADE';

Резултатът е :

5

Нека да разгледаме случай, при който искаме да изтрием запис, а към него има референция (външен ключ) с ограничение ON DELETE RESTRICT. В такъв случай добра стратегия е first update then delete. Навсякъде, където съществува като запис референция към записа, който искаме да изтрием, правим промяна с друга референция. Уверяваме се, че вече никой не сочи към желания от нас ред и тогава го трием. Може да разиграете случая, при който се изтрива спортна група. Трябва да помислите за правилно преместване на всички студенти, трениращи в нея в друга спортна група, вероятно от същия тип спорт. Тази таблица обаче се реферира и от taxpayments. В този случай бихме изгубили данните за това за коя група са платили дадените ученици таксите си. Ако няма проблем да променим и там групата на друга група и платените такси за старата група да започнат да важат за новата, правим го и после трием групата. Но ако има – просто няма смисъл групата да бъде изтривана – това ще доведе до загуба на данни.

 

Даниел Джолев

Leave a Reply

Your email address will not be published. Required fields are marked *