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

СУБД/Практические занятия №2(2 семестр)

Материал из Wiki

Перейти к: навигация, поиск

Содержание

Триггеры (MySQL)

CREATE
    [DEFINER = { USER | CURRENT_USER }]
    TRIGGER trigger_name
    trigger_time trigger_event
    ON tbl_name FOR EACH ROW
    trigger_body
 
trigger_time: { BEFORE | AFTER }
 
trigger_event: { INSERT | UPDATE | DELETE }

Триггеры это особые процедуры которые срабатывают при изменении данных в таблицы операторами INSERT, UPDATE и DELETE.

Триггеры могут срабатывать как до BEFORE так и после AFTER изменения таблицы. Получается, что триггер может быть в 6 состояниях.
	INSERT (BEFORE | AFTER)
	UPDATE (BEFORE | AFTER)
	DELETE (BEFORE | AFTER)

Что необходимо для работы (ограничения, не все)

  • Во-первых, триггеры появились в MySQL 5.0.2.
  • Во-вторых, триггеры в MySQL могут создаваться только пользователем с привилегией SUPER, т.е. если Ваше приложение использует триггеры, то ни на одном, даже платном, хостинге оно работать не будет!(база для выполнения примеров и пользователь `студент` имеют эти права).
    • В качестве альтернативы, DBA может устанавливать переменную системы log_bin_trust_function_creators в 1, что позволяет любому со стандартной привилегией CREATE ROUTINE создавать сохраненные функции.
  • В-третьих, нельзя редактировать таблицу уже открытую для чтения или записи условием инициализации триггера. В этом случае просто используйте оператор SET для нужных полей.
CREATE TRIGGER `artists_litera_autoupdate` AFTER UPDATE ON `cms_artists`
   FOR EACH ROW BEGIN
      UPDATE `cms_artists` SET `litera` = UPPER(SUBSTRING(NEW.`name`, 1, 1)) WHERE `id` = OLD.`id`;
   END;
В противном случае Вы получите такую ошибку: 
#1442 - Can't update table 'tablename' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
  • Кроме того в показанном выше запросе есть ещё одна ошибка, которая после исправления предыдущей обязательно появится:
ERROR 1362 (HY000): Updating of NEW row is not allowed in after trigger
  • В-четвёртых, значения надо менять до обновления. В конечном счёте правильный вариант:
CREATE TRIGGER `artists_litera_autoupdate` BEFORE UPDATE ON `cms_artists`
 FOR EACH ROW BEGIN
    SET NEW.`litera`= UPPER(SUBSTRING(NEW.`name`, 1, 1));
 END;
  • В-пятых, при очищении таблицы (TRUNCATE) триггер, повешенный на событие DELETE, исполняться не будет.
  • В MySQL нет проблемы с рекурсивными триггерами! Разработчики MySQL просто лочат изменяемую таблицу даже на уровне триггера.
DELIMITER $$
CREATE DEFINER=`root`@`%` TRIGGER mytrigger
BEFORE INSERT ON mytable
FOR each ROW
BEGIN
	UPDATE mytable
	   SET a = 123
	WHERE (id=1);
END$$

Nuvola apps error.png Выполняться не будет

  • MySQL диалект триггеров несколько отличается от диалекта PostgreSQL:
    • Не обязательно объявлять переменные в начале процедуры;
    • Прервать выполнение триггера нельзя, он должен отработать полностью;
    • Как было сказано выше, рекурсии мы не боимся, поэтому лишние проверки и дополнительные поля нам не потребуются.
  • Поэтому несколько изменяется логика:
    • Переменные появляются по ходу кода;
    • Вместо возвратов из триггера, оборачиваем код условиями;
    • Все изменения, касающиеся структуры дерева, применяем к вспомогательной объединенной таблице;
  • 200px-Yes check.pngВ MySQL имеет значение порядок перечисления полей в запросе UPDATE, так как поля изменяемые во время запроса в самом же запросе меняют значение на новое, поэтому, если мы дальше по запросу используем эти поля в условиях, результат будет неадекватным.

Добавление триггера

Для того чтобы понять работу триггера рассмотрим пример.

Задание
Необходимо при добавлении записи в табл user, пароль преобразовывать в хеш md5(), также имя и отчество преобразовывать в инициалы.
  • Решение
	 DELIMITER //
	 CREATE TRIGGER `test_user_pass` BEFORE INSERT ON `test`.`user`
	     FOR EACH ROW
	     BEGIN
	      SET NEW.name = LEFT(NEW.name,1);
	      SET NEW.otch = LEFT(NEW.otch,1);
	      SET NEW.pass = md5(NEW.pass);
	     END//
 DELIMITER ;

Теперь вставляем туда запись
INSERT  INTO `user` SET `fam`='Нагайченко', `name`='Максим', `otch` = 'Валерьевич', `pass` = 'password', `login` = 'maxnag';

Что теперь в таблице mysql>
SELECT * FROM `user`;
	+----+-------------+------+------+----------------------------------+--------+
	| id | fam         | name | otch | pass                             | login  |
	+----+-------------+------+------+----------------------------------+--------+
	|  1 | Нагайченко  | M    | В    | 5f4dcc3b5aa765d61d8327deb882cf99 | maxnag |
	+----+-------------+------+------+----------------------------------+--------+
 
	1 row in set (0.00 sec)


Как видно всего несколькими строчками можно убрать целые методы, которые мы использовали при регистрации нового пользователя. Теперь еще надо создать триггер на UPDATE таблицы, с таким же телом, чтобы пользователь не смог записать полное имя, отчество и пароль не в МД5();

Создание триггера на BEFORE UPDATE
	 DELIMITER //
	 CREATE TRIGGER `test_user_pass2` BEFORE UPDATE ON `test`.`user`
	    FOR EACH ROW
	     BEGIN
	      SET NEW.name = LEFT(NEW.name,1);
	      SET NEW.otch = LEFT(NEW.otch,1);
	      SET NEW.pass = md5(NEW.pass);
	     END//
	 DELIMITER ;
Обновление записи
UPDATE `user` SET `fam`='Иванов', `name`='Иван', `otch` = 'Иванович', `pass` = 'пароль', `login` = 'ivan' WHERE id=1;

Итог1
SELECT * FROM `user`;
2	+----+-------------+------+------+----------------------------------+--------+
3	| id | fam         | name | otch | pass                             | login  |
4	+----+-------------+------+------+----------------------------------+--------+
5	|  1 | Иванов      | И    | И    | e242f36f4f95f12966da8fa2efd59992 | ivan   |
6	+----+-------------+------+------+----------------------------------+--------+
7	 
8	1 row in set (0.00 sec)
Могут возникнуть вопросы, что такое NEW в теле триггера,
  • NEW — для доступа к новым записям
  • OLD — для доступа к старым записям

Bombilla amarilla - yellow Edison lamp.png Например, если я обновил фамилию, то новое значение мне доступно через NEW.fam, а к старому OLD.fam

Список триггеров

Как получить список всех триггеров существующих в текущей базе данных?
SHOW TRIGGERS;

Как удалить триггер из текущей базы данных?

DROP TRIGGER `name`;

200px-Yes check.pngПосле удаления таблицы, к которой привязан триггер, не стоит пытаться удалить его (триггер) — MySQL сделает это автоматически.

Транзакции в MySQL

Рассмотрим механизм транзакций. Для начала приведем один простой пример.
UPDATE user_account SET allsum=allsum + 1000 WHERE id='1';
UPDATE user_account SET allsum=allsum - 1000 WHERE id='2';

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

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

Оператор открывающий транзакцию в MySQL:

  • "START TRANSACTION;"

После правильного выполнения всех запросов транзакцию можно либо завершить внеся все изменения в базу данных -

  • "COMMIT;"

либо откатить вернув все в начальное состояние -

  • "ROLLBACK".

Если конкретно рассматривать случай с базой MySQL то тут есть несколько подводных камней.

  1. В MySQL существует несколько типов таблиц. Это ISAM, HEAP, MyISAM, InnoDB, BDB и т.д. Транзакционный механизм поддерживают только InnoDB и BDB. Поэтому все таблицы с которыми вы хотите работать через транзакции следует переконвертировать в соответствующий тип.
  2. По умолчанию MySQL работает в режиме autocommit. Это означает, что результаты выполнения любого SQL-оператора, изменяющего данные, будут сразу сохраняться.

Режим autocommit можно отключить командой SET AUTOCOMMIT=0. При отключенном режиме autocommit каждую транзакцию надо явно завершать операторами COMMIT / ROLLBACK.

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

START TRANSACTION;
 UPDATE user_account SET allsum=allsum + 1000 WHERE id='1';
 UPDATE user_account SET allsum=allsum - 1000 WHERE id='2';
 COMMIT;