Извините, что снова задаю этот вопрос, так как на форуме так много об этом. но я надеюсь, что моя проблема отличается от других. и извините за мой плохой английский.
Сначала у меня есть 2 таблицы (родительский и дочерний) Родитель (местоположение)
Ребенок (область)
это мой триггер после удаления.
CREATE TRIGGER after_delete_area_location
AFTER DELETE
ON area FOR EACH ROW
BEGIN
UPDATE location SET loc_size = loc_size + old.ar_size WHERE loc_id=old.ar_loc_id
END;
Например, если я хочу удалить ar_id = '2', тогда ar_size будет обновлен в местоположении loc_size.
поскольку значение loc_size равно «0», то после запуска триггера значение будет равно 40.
проблема в том, что триггер работает неправильно, loc_size вообще не обновляется, просто значение «0» после запуска триггера.
что мне не хватает или что происходит, так как он вообще не работает.
пожалуйста, помогите мне решить эту проблему. Большое спасибо заранее.
сделано, обновлено и бросить отвлечение
Опубликованный запрос синтаксически неверен, поскольку в операторе обновления отсутствует терминатор - то, что вам скажет любой ide, и вам может потребоваться установить разделители.
Спасибо @P.Salmon за информацию о разделителях. Но это не проблема в моем запросе. так как он работает хорошо, но нет результата для получения значения из old.ar_size в loc_size
Я не вижу проблемы в вашем триггере. Я протестировал его, и он работает. Чтобы сделать его полным, я добавил триггер вставки в таблицу area
.
create table location(loc_id int,loc_size int);
create table area(ar_id int,ar_loc_id int,ar_size int);
delimiter //
CREATE TRIGGER after_insert_area_location
AFTER insert
ON area FOR EACH ROW
BEGIN
UPDATE location SET loc_size = loc_size - new.ar_size WHERE loc_id=new.ar_loc_id;
-- Note: In reality, you should throw in an if statement before the UPDATE to make sure there is enough loca_size to be taken away by the ar_size.
END//
CREATE TRIGGER after_delete_area_location
AFTER DELETE
ON area FOR EACH ROW
BEGIN
UPDATE location SET loc_size = loc_size + old.ar_size WHERE loc_id=old.ar_loc_id;
END//
delimiter ;
insert into location values(1,100),(2,75);
select * from location;
+--------+----------+
| loc_id | loc_size |
+--------+----------+
| 1 | 100 |
| 2 | 75 |
+--------+----------+
select * from area;
Empty set (0.00 sec)
-- Test the insert
insert into area values(1,2,35),(2,2,40);
select * from area;
+-------+-----------+---------+
| ar_id | ar_loc_id | ar_size |
+-------+-----------+---------+
| 1 | 2 | 35 |
| 2 | 2 | 40 |
+-------+-----------+---------+
select * from location;
+--------+----------+
| loc_id | loc_size |
+--------+----------+
| 1 | 100 |
| 2 | 0 |
+--------+----------+
-- test the delete
delete from area where ar_id=2;
select * from area;
+-------+-----------+---------+
| ar_id | ar_loc_id | ar_size |
+-------+-----------+---------+
| 1 | 2 | 35 |
+-------+-----------+---------+
select * from location;
+--------+----------+
| loc_id | loc_size |
+--------+----------+
| 1 | 100 |
| 2 | 40 |
+--------+----------+
Как видите, как в операциях вставки, так и в операциях удаления значения были обновлены соответствующим образом.
-- ОБНОВЛЕНО ЗДЕСЬ --
Вот усиленная версия триггера after_insert_area_location
, в которой я продемонстрирую недавно добавленный оператор IF для сохранения целостности данных. Обратите внимание, что статус SIGNAL
используется для создания ошибки, которая служит предупреждением, а также прерывает операцию.
-- First of all, I truncated my table so we can start from scratch.
-- Make sure the original trigger is dropped before creating the newer one to avoid conflicts.
drop trigger after_insert_area_location //
CREATE TRIGGER after_insert_area_location
AFTER insert
ON area FOR EACH ROW
BEGIN
if (select loc_size from location where loc_id=new.ar_loc_id) - new.ar_size < 0 then
signal sqlstate '77777' set message_text='You cannot take more land than you have'; -- Note: the sqlstate code and the message_text can be modified to your liking
end if;
UPDATE location SET loc_size = loc_size - new.ar_size WHERE loc_id=new.ar_loc_id;
END//
delimiter ;
-- then let's populate the location table:
insert into location values(1,100),(2,75);
select * from location;
+--------+----------+
| loc_id | loc_size |
+--------+----------+
| 1 | 100 |
| 2 | 75 |
+--------+----------+
select * from area;
Empty set (0.00 sec)
-- Now we test the insert one row at a time:
insert into area values(1,2,35);
select * from location;
+--------+----------+
| loc_id | loc_size |
+--------+----------+
| 1 | 100 |
| 2 | 40 |
+--------+----------+
select * from area;
+-------+-----------+---------+
| ar_id | ar_loc_id | ar_size |
+-------+-----------+---------+
| 1 | 2 | 35 |
+-------+-----------+---------+
-- now we add another row with an ar_size more than its location can afford:
insert into area values(2,2,80);
ERROR 1644 (77777): You cannot take more land than you have
Как видите, оператор SIGNAL
в операторе IF
триггера вызывает ошибку с кодом SQL_STATE 77777
и сообщением, которые предварительно заданы. Это отменяет изменения, внесенные с момента вставки новой строки.
select * from location;
+--------+----------+
| loc_id | loc_size |
+--------+----------+
| 1 | 100 |
| 2 | 40 |
+--------+----------+
select * from area;
+-------+-----------+---------+
| ar_id | ar_loc_id | ar_size |
+-------+-----------+---------+
| 1 | 2 | 35 |
+-------+-----------+---------+
Короче говоря, мы можем использовать оператор IF в триггере, чтобы осуществлять некоторый контроль над потоком данных. А оператор SIGNAL
можно использовать для преднамеренного вызова ошибки, чтобы остановить/отменить выполнение триггера, а также операцию, которая инициировала триггер. Как указано выше, не только UPDATE
в триггере не был выполнен, но и оператор insert
, который вызвал триггер, также был отменен.
Спасибо @blabla_bingo, ваш ответ правильный. но для меня это не работает. код также такой же, как вы делаете (вставляете и удаляете), возвращая/обновляя значение в месте, где col loc_size не получил значение из old.ar_size. я не знаю ошибку, так как в журнале нет ошибок, потому что он работает хорошо, ошибок нет вообще. с примечанием, которое Вы мне дали, я этого не делал, потому что я не знаю, что оператор if может работать внутри триггера. Не могли бы Вы помочь мне с запиской, которую Вы упомянули выше. большое спасибо
@me.theblackz Проверьте обновление, сделанное для вас.
Большое спасибо за оператор if. Никогда не думал, что так можно сделать. большое спасибо за знания. поэтому, если я введу оператор if после удаления, который содержит вот так if (select ar_size from area where ar_loc_id=old.loc_id) = 0 then update location set loc_size=loc_size+old.ar_size where loc_id=old.ar_loc_id endif;
, можно ли это сделать так же после удаления? просто проверяя, не равен ли он 0 в ar_size, он должен отправлять значение до нуля. любое предложение по этому поводу?
Я бы учитывал возможность нулей в обоих терминах, объединяя их с 0..
@me.theblackz это утверждение if (select ar_size from area where ar_loc_id=old.loc_id) = 0 then update location set loc_size=loc_size+old.ar_size where loc_id=old.ar_loc_id endif;
сбивает с толку, поскольку у вас есть и old.loc_id, и old.ar_size, которые являются взаимоисключающими, поскольку вы можете использовать только ключевые слова old
и new
для столбцов в сработавшей таблице.
большое спасибо @blabla_bingo.. и извините за медленный ответ.. я был так занят другими. так что прямо сейчас я делаю новый mysql, так как он не работает. создал новую базу данных и таблицы, и это работает как по волшебству. не знаю проблемы есть.
Пожалуйста, опубликуйте триггер удаления полностью вместе с примером оператора удаления. Кстати, дискуссия о вставке триггеров обновления является бессмысленным отвлечением.