LINUX.ORG.RU

[sql] вопрос

 


0

1

Здравствуйте!
Помогите, пожалуйста, с SQL'ем.
Есть 2 таблицы (A и B). У A есть foreign key на поле X таблицы B.
Сейчас, если попытаться вставить в таблицу А запись, то может случится «foreign key constraint fails».

Задача стоит такая: производится запись в таблицу A. Поле X имеет вид <что-то>#<суфикс>. Сейчас такая запись не пройдет, если <что-то>#<суфикс> нет в таблице B.
Требуется, чтобы такое поведение и осталось, за одним исключением - если в записи присутствует #<суфикс>, то делается проверка на <что-то> (без #<суфикс>) в таблице B, и если такая запись есть, то разрешать вставлять в таблицу A. Если #<суфикс> нет, то делается обычная проверка.

Foreign key, я так понимаю, надо убрать. Но тогда куча левого вставляться будет. Stored Procedures? Или как лучше сделать?

PS: сумбурно, извините

★★★★★

Не особо знаю БД, но, кажется, БД не нормализована.
По-хорошему стоит разделить это поле на два и сделать ключ из двух полей.
А уже при выборках соединять в одно, если нужно.

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

schizoid ★★★
()
Ответ на: комментарий от kovrik

Синтаксис зависит от СУБД, нужно смотреть в документации.

В общем выглядит как-то так:

CREATE TRIGGER MyTrigger
    BEFORE INSERT ON MyTable
    REFERENCING NEW ROW AS n
    FOR EACH ROW
    IF SUBSTR(n.key, POS(n.key, '#')) IN (SELECT key FROM SecondTable) THEN
       -- Warning or rollback
 
    END IF;
;
При попытке вставить новую запись, проверяется наличие первой части ключа (до '#') в таблице SecondTable (для примера).

schizoid ★★★
()
Ответ на: комментарий от schizoid

О, спасибо большое, буду ковырять в этом направлении!

kovrik ★★★★★
() автор топика
Ответ на: комментарий от schizoid

Что-то не создается триггер, пишет, что синтаксис запроса неверный.

CREATE TRIGGER checkSubRefs
    BEFORE INSERT ON stats
    REFERENCING NEW ROW AS n
    FOR EACH ROW
    IF NOT SUBSTR(n.referrerID, POS(n.referrerID, '#')) IN (SELECT id FROM referrer) THEN
		ROLLBACK; 	
    END IF;
;
Что не так?

kovrik ★★★★★
() автор топика
Ответ на: комментарий от kovrik

Это - только пример, по памяти писал (кажется, из mysql), фактически - псевдокод :)

Посмотри в документации к своей СУБД синтаксис создания триггеров.

schizoid ★★★
()
Ответ на: комментарий от schizoid

В общем, получился такой триггер:

CREATE TRIGGER CheckSubRefs
BEFORE INSERT ON stats
FOR EACH ROW
BEGIN
     IF NOT (SUBSTRING(NEW.referrerID, POS(NEW.referrerID, '#')) IN (SELECT id FROM referrer)) THEN
        ROLLBACK;
     END IF;
END$$
Одна проблема - ROLLBACK нельзя использовать в триггерах. Как тогда сделать?

kovrik ★★★★★
() автор топика
Ответ на: комментарий от kovrik

Сделал так (все работает):

CREATE TRIGGER CheckSubRefs
BEFORE INSERT ON stats
FOR EACH ROW
BEGIN
	IF NOT EXISTS (SELECT id AS refid FROM referrer WHERE referrer.id=NEW.referrerID) THEN
	     IF (SUBSTRING(NEW.referrerID, LOCATE(NEW.referrerID, '#')) IN (SELECT id FROM referrer)) THEN
        	CALL raiseException;
	     END IF;
	END IF;
END;
Процедуры raiseException не существует -> вылезает эксэпшн и транзакция отменяется.
Работать работает, только вот безопасно ли так делать?

kovrik ★★★★★
() автор топика
Ответ на: комментарий от kovrik

На stackoverflow говорят, что отменить INSERT в триггере MySQL не позволяет никак.

Единственное решение - в случае ошибки ключей вставлять что-то вроде пустой строки (т.е. заполнить все поля NULL или 0) и выдавать сообщение об ошибке (исключением или завести для этого отдельную таблицу errors), а уже в программе проверять ошибки.

schizoid ★★★
()
Ответ на: комментарий от schizoid

Ммм..можно про отлов исключений в MySQL'е чуть подробнее?
Конкретно в данном триггере как ловить исключение (в общем-то, никаких больше действий предпринимать не надо).

kovrik ★★★★★
() автор топика
Ответ на: комментарий от kovrik

В данном триггере ловить его не рекомендуется - наоборот, его нужно из триггера бросать (по-хорошему).

А там, где используется insert (storedproc или программа), его нужно ловить:

- Для процедур нужно объявить обработчик этого исключения.

- В зависимости от языка основной программы (и наличия в нём механизма исключений) ловить через try-catch блок: пример для Python, пример для VB :)

Но, если уж очень надо, можно сделать обработчик исключения прямо в триггере.

schizoid ★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.