|
|
@@ -0,0 +1,118 @@
|
|
|
+# Хранимые процедуры. Триггеры.
|
|
|
+
|
|
|
+## Хранимые процедуры
|
|
|
+
|
|
|
+**Хранимая процедура** - это способ инкапсуляции повторяющихся действий. В хранимых процедурах можно объявлять переменные, управлять потоками данных, а также применять другие техники программирования.
|
|
|
+
|
|
|
+**За**
|
|
|
+
|
|
|
+- Разделение логики с другими приложениями. Хранимые процедуры инкапсулируют функциональность; это обеспечивает связность доступа к данным и управления ими между различными приложениями.
|
|
|
+- Изоляция пользователей от таблиц базы данных. Это позволяет давать доступ к хранимым процедурам, но не к самим данным таблиц.
|
|
|
+- Обеспечивает механизм защиты. В соответствии с предыдущим пунктом, если вы можете получить доступ к данным только через хранимые процедуры, никто другой не сможет стереть ваши данные через команду SQL DELETE.
|
|
|
+- Улучшение выполнения как следствие сокращения сетевого трафика. С помощью хранимых процедур множество запросов могут быть объединены.
|
|
|
+
|
|
|
+**Против**
|
|
|
+
|
|
|
+- Повышение нагрузки на сервер баз данных в связи с тем, что большая часть работы выполняется на серверной части, а меньшая - на клиентской.
|
|
|
+- Придется много чего подучить. Вам понадобится выучить синтаксис MySQL выражений для написания своих хранимых процедур.
|
|
|
+- Вы дублируете логику своего приложения в двух местах: серверный код и код для хранимых процедур, тем самым усложняя процесс манипулирования данными.
|
|
|
+- Миграция с одной СУБД на другую (DB2, SQL Server и др.) может привести к проблемам.
|
|
|
+
|
|
|
+**Ограничитель (Delimiter)** - это символ или строка символов, который используется для указания клиенту MySQL, что вы завершили написание выражения SQL. В обычных запросах в качестве ограничителя используется символ точки с запятой. Тем не менее, могут возникнуть проблемы, так как в хранимой процедуре может быть несколько выражений, каждое из которых должно заканчиваться точкой с запятой. В этом уроке я использую символ `|` в качестве ограничителя.
|
|
|
+
|
|
|
+## Триггеры
|
|
|
+
|
|
|
+<!-- https://www.digitalocean.com/community/tutorials/how-to-manage-and-use-mysql-database-triggers-on-ubuntu-18-04-ru -->
|
|
|
+
|
|
|
+**Триггер** — это хранимая откомпилированная SQL-процедура, которая не вызывается непосредственно, а исполняется при наступлении определенного события внутри базы данных (вставки, удаления, обновления записей).
|
|
|
+
|
|
|
+Хранимые процедуры запускают во всех средах, и нет необходимости перестроения логики. С того момента как вы создали хранимую процедуру, не важно какое приложение вы используете для вызова процедуры. Также не важно на каком языке вы программируете, логика процедуры содержится на сервере БД.
|
|
|
+
|
|
|
+Также хранимые процедуры могут сократить сетевой трафик. Сложные, повторяющиеся задачи можно обрабатывать с помощью процедур на сервере Баз данных, без необходимости отсылки промежуточных результатов приложению.
|
|
|
+
|
|
|
+Триггеры применяются для обеспечения целостности данных и реализации сложной бизнес-логики. Триггер запускается сервером автоматически при попытке изменения данных в таблице, с которой он связан. Все производимые им модификации данных рассматриваются как выполняемые в транзакции, в которой выполнено действие, вызвавшее срабатывание триггера. Соответственно, в случае обнаружения ошибки или нарушения целостности данных может произойти откат этой транзакции.
|
|
|
+
|
|
|
+Общий вид синтаксиса для создания тригера:
|
|
|
+
|
|
|
+```sql
|
|
|
+CREATE TRIGGER
|
|
|
+ trigger_name trigger_time trigger_event
|
|
|
+ ON tbl_name FOR EACH ROW trigger_body
|
|
|
+```
|
|
|
+
|
|
|
+где:
|
|
|
+
|
|
|
+- *trigger_name* - название триггера (уникальное в пределах БД);
|
|
|
+- *trigger_time* - время срабатывания триггера. **BEFORE** — перед событием. **AFTER** — после события;
|
|
|
+- *trigger_event* - событие:
|
|
|
+ * *insert* — событие возбуждается операторами *insert*, *data load*, *replace*;
|
|
|
+ * *update* — событие возбуждается оператором *update*;
|
|
|
+ * *delete* — событие возбуждается операторами *delete*, *replace*. Операторы *DROPTABLE* и *TRUNCATE* не активируют выполнение триггера;
|
|
|
+
|
|
|
+ Важно понимать, что *trigger_event* не представляет литеральный тип инструкции SQL, которая активизирует триггер. Например, триггер INSERT активизируется не только инструкцией INSERT, но и LOAD DATA, потому что обе инструкции вставляют строки в таблицу.
|
|
|
+
|
|
|
+- *tbl_name* — название таблицы;
|
|
|
+- *trigger_body* — выражение, которое выполняется при активации триггера. Если Вы хотите выполнять много инструкций, используйте операторную конструкцию `BEGIN ... END`. Это также дает возможность Вам использовать те же самые инструкции, которые являются допустимыми внутри сохраненных подпрограмм.
|
|
|
+
|
|
|
+Не может быть двух триггеров для данной таблицы, которые имеют те же самые время действия и событие. Например, Вы не можете иметь два триггера BEFORE UPDATE для таблицы. Но Вы можете иметь BEFORE UPDATE и BEFORE INSERT или BEFORE UPDATE и AFTER UPDATE.
|
|
|
+
|
|
|
+Триггеры могут быть привязаны не к таблице, а к представлению (VIEW). В этом случае с их помощью реализуется механизм "обновляемого представления".
|
|
|
+
|
|
|
+Например: создадим две таблицы **test** и **log**, напишем триггер, который после добавления записи в таблицу **test** будет вести лог этого события:
|
|
|
+
|
|
|
+```sql
|
|
|
+-- таблица, за которой мы будем следить
|
|
|
+CREATE TABLE `test` (
|
|
|
+ `id` INT( 11 ) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
|
|
+ `content` TEXT NOT NULL
|
|
|
+);
|
|
|
+
|
|
|
+-- лог
|
|
|
+CREATE TABLE `log` (
|
|
|
+ `id` INT( 11 ) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
|
|
+ `msg` VARCHAR( 255 ) NOT NULL,
|
|
|
+ `time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
+ `row_id` INT( 11 ) NOT NULL
|
|
|
+);
|
|
|
+
|
|
|
+-- триггер
|
|
|
+DELIMITER |
|
|
|
+CREATE TRIGGER `update_test` AFTER INSERT ON `test`
|
|
|
+FOR EACH ROW BEGIN
|
|
|
+ INSERT INTO log Set msg = 'insert', row_id = NEW.id;
|
|
|
+END;
|
|
|
+|
|
|
|
+```
|
|
|
+
|
|
|
+Здесь оператор DELIMITER служит для определения литерала начала/окончания процедуры и может состоять более, чем из одного символа (необходимо выбирать разделитель, который не будет использоваться в процедуре).
|
|
|
+
|
|
|
+На столбцы таблицы, к которой привязан триггер можно ссылаться с помощью псевдонимов **OLD** и **NEW**. `OLD.col_name` указывает на столбец с именем *col_name* **до** изменения или удаления данных. `NEW.col_name` относится к колонке новой строки после вставки или существующей - сразу после её обновления.
|
|
|
+
|
|
|
+Триггеры имеют несколько важных особенностей использования:
|
|
|
+
|
|
|
+- триггеры могут создаваться только пользователем с привилегией SUPER;
|
|
|
+- при использовании запроса, затрагивающего N - записей, триггер будет запускаться N - раз;
|
|
|
+- после удаления таблицы, СУБД MySQL автоматически удаляет привязанные к ней триггеры.
|
|
|
+
|
|
|
+```sql
|
|
|
+create trigger
|
|
|
+sale_item
|
|
|
+before insert
|
|
|
+on sales
|
|
|
+for each row
|
|
|
+ update warehouse set quantity=quantity - NEW.quantity where id=NEW.item_id;
|
|
|
+```
|
|
|
+
|
|
|
+```sql
|
|
|
+delimiter $$
|
|
|
+CREATE trigger
|
|
|
+update_warehouse
|
|
|
+before update
|
|
|
+on warehouse
|
|
|
+for EACH row begin
|
|
|
+ if NEW.quantity<0 THEN
|
|
|
+ SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'На складе недостаточно товара';
|
|
|
+ END if;
|
|
|
+end;
|
|
|
+$$
|
|
|
+```
|