Глава 21. Сохраненные программы и представления

Эта глава обсуждает сохраненные программы и представления, которые являются объектами базы данных, определенными с точки зрения кода SQL, который сохранен на сервере для более позднего выполнения.

Сохраненные программы включают эти объекты:

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

Эта глава описывает, как использовать сохраненные программы и представления. Следующие разделы обеспечивают дополнительную информацию о синтаксисе SQL для запросов, связанных с этими объектами:

В MySQL метаданные изменяющиеся у объектов, упомянутых сохраненными программами, обнаружены и вызывают автоматический перепарсинг затронутых запросов, когда программа затем выполнена. Для получения дополнительной информации см. раздел 9.10.4.

21.1. Определение сохраненных программ

Каждая сохраненная программа содержит тело, которое состоит из запроса SQL.Это запрос может быть составным запросом, составленным из нескольких, отделенных точкой с запятой (;). Например, тело следующей хранимой процедуры составляет блок BEGIN ... END, который содержит запрос SET и цикл REPEAT, который непосредственно содержит другой SET:

CREATE PROCEDURE dorepeat(p1 INT)
BEGIN
  SET @x = 0;
  REPEAT SET @x = @x + 1; UNTIL @x > p1 END REPEAT;
END;
Если Вы используете mysql, чтобы определить сохраненную программу, содержащую символы точки с запятой, возникает проблема. По умолчанию mysql признает точку с запятой разделителем запроса, таким образом, Вы должны пересмотреть разделитель временно, чтобы заставить mysql передавать все сохраненное определение программы серверу.

Чтобы пересмотреть разделитель, используйте команду delimiter. Следующий пример показывает, как сделать это для процедуры dorepeat(). Разделитель изменен на //, чтобы позволить всему определению быть переданным к серверу как единственный запрос, а затем ; восстанавливается прежде, чем вызвать процедуру. Это включает разделитель ;, используемый в теле процедуры, который передается к серверу вместо того, чтобы интерпретироваться непосредственно mysql.

mysql> delimiter //
mysql> CREATE PROCEDURE dorepeat(p1 INT)
    -> BEGIN
    ->   SET @x = 0;
    ->   REPEAT SET @x = @x + 1; UNTIL @x > p1 END REPEAT;
    -> END
    -> //
Query OK, 0 rows affected (0.00 sec)

mysql> delimiter ;
mysql> CALL dorepeat(1000);
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @x;
+------+
| @x   |
+------+
| 1001 |
+------+
1 row in set (0.00 sec)
Вы можете переделать разделитель на строку вовсе не обязательно именно //, разделитель может состоять из одного или нескольких символов. Вы должны избегать использования наклонной черты влево (\), потому что это символ ESC для MySQL.

Следующее пример функции, которая берет параметр, выполняет работу, используя функцию SQL, и возвращает результат. В этом случае не нужно использовать delimiter, потому что функциональное определение не содержит внутренний разделитель запроса:

mysql> CREATE FUNCTION hello (s CHAR(20))
mysql> RETURNS CHAR(50) DETERMINISTIC
    -> RETURN CONCAT('Hello, ',s,'!');
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT hello('world');
+----------------+
| hello('world') |
+----------------+
| Hello, world!  |
+----------------+
1 row in set (0.00 sec)

21.2. Использование сохраненных подпрограмм (процедуры и функции)

MySQL поддерживает сохраненные подпрограммы (процедуры и функции). Сохраненная подпрограмма ряд запросов SQL, которые могут быть сохранены на сервере. Как только это было сделано, клиенты могут обратиться к сохраненной подпрограмме напрямую.

Сохраненные подпрограммы могут быть особенно полезными в определенных ситуациях:

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

Сохраненные подпрограммы также позволяют Вам иметь библиотеки функций на сервере базы данных. Это особенность, совместно использованная современными языками приложений, которые включают такое внутренне (например, при использовании классов). Использование этих языковых функций приложения-клиента выгодно для программиста даже вне контекста использования базы данных.

MySQL следует за синтаксисом SQL:2003 для сохраненных подпрограмм, который также используется DB2 IBM. Весь синтаксис, описанный здесь, поддерживается, любые ограничения и расширения зарегистрированы.

Дополнительные ресурсы

21.2.1. Синтаксис подпрограмм

Сохраненная подпрограмма процедура или функция. Сохраненные подпрограммы создаются с помощью CREATE PROCEDURE и CREATE FUNCTION (см. раздел 14.1.13 ). Процедура вызвана, используя CALL (см. раздел 14.2.1), и может вернуть значения, используя только выходные переменные. Функция может быть вызвана из запроса точно так же, как любая другая функция (то есть, обращаясь к имени функции) и может возвратить скалярное значение. Тело сохраненной подпрограммы может использовать составные запросы (см. раздел 14.6).

Сохраненные подпрограммы могут быть удалены через DROP PROCEDURE и DROP FUNCTION (см. раздел 14.1.23) и изменены с помощью ALTER PROCEDURE и ALTER FUNCTION (см. раздел 14.1.5).

Хранимая процедура или функция связана с особой базой данных. У этого есть несколько значений:

Сохраненные функции не могут быть рекурсивными.

Рекурсия в хранимых процедурах разрешена, но отключена по умолчанию. Чтобы включить рекурсии, установите переменную max_sp_recursion_depth к значению больше ноля. Рекурсия хранимой процедуры увеличивает требование к пространству стека потока. Если Вы увеличиваете значение max_sp_recursion_depth, может быть необходимо увеличить размер стека потока, увеличивая значение thread_stack при запуске сервера. См. раздел 6.1.5.

MySQL поддерживает очень полезное расширение, которое включает использование регулярных запросов SELECT (то есть, не используя курсоры или местные переменные) в хранимой процедуре. Набор результатов такого запроса просто посылают непосредственно клиенту. Многократный SELECT производит много наборов результатов, таким образом, клиент должен пользоваться библиотекой клиента MySQL, которая поддерживает многократные наборы результатов. Это означает, что клиент должен пользоваться библиотекой клиента от версии MySQL, по крайней мере, 4.1. Клиент должен также определить опцию CLIENT_MULTI_RESULTS, когда соединяется. Для программ C это может быть сделано с помощью функции mysql_real_connect() C API. См. разделы 25.8.7.54 и 25.8.17.

21.2.2. Сохраненные подпрограммы и привилегии MySQL

Система привилегий MySQL принимает сохраненные подпрограммы во внимание следующим образом:

Сервер управляет таблицей mysql.proc в ответ на запросы, которые создают, изменяют или удаляют сохраненные подпрограммы. Не поддержано, чтобы сервер замечал ручные манипуляции с этой таблицей.

21.2.3. Сохраненные метаданные

Метаданные о сохраненных подпрограммах могут быть получены следующим образом:

21.2.4. Хранимые процедуры, функции, триггеры и LAST_INSERT_ID()

В пределах тела сохраненной подпрограммы (процедуры или функции) или триггера, значение LAST_INSERT_ID() изменяется тем же самым путем, что касается запрсов, выполненных вне тела этих видов объектов (см. раздел 13.14). Эффект сохраненной подпрограммы или триггера на значение LAST_INSERT_ID() зависит от вида подпрограммы:

21.3. Использование триггеров

Триггер названный объект базы данных, который связан с таблицей активируется, когда особое событие имеет место для таблицы. Некоторое использование для триггеров должно выполнить проверки значений, которые будут вставлены в таблицу или выполнят вычисления на значениях, вовлеченных в обновление.

Триггер определен, чтобы активироваться, когда запрос вставляет, обновляет или удаляет строки в связанной таблице. Эти операции события триггера. Например, строки могут быть вставлены INSERT или LOAD DATA, и триггер вставки активируется для каждой вставленной строки. Триггер может быть установлен на активацию прежде или после события. Например, у Вас может быть триггер, который активируется перед каждой строкой, которая вставлена в таблицу или после каждой строки, которая обновлена.

Триггеры MySQL активируются только для изменений, произведенных в таблицах запросами SQL. Они не активируются для изменений в представлениях или изменений таблиц, сделанных API, которые не передают запроса SQL серверу MySQL. Это означает, что триггеры не активированы изменениями в таблицах INFORMATION_SCHEMA или performance_schema, потому что эти таблицы фактически представления.

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

21.3.1. Синтаксис и примеры триггеров

Чтобы создать или удалить триггер, используйте CREATE TRIGGER или DROP TRIGGER, описанные в разделах 14.1.17 и 14.1.27.

Вот простой пример, который связывает триггер с таблицей, чтобы активироваться для вызова INSERT. Этот триггер действует как сумматор, суммируя значения, вставленные в один из столбцов таблицы.

mysql> CREATE TABLE account (acct_num INT, amount DECIMAL(10,2));
Query OK, 0 rows affected (0.03 sec)

mysql> CREATE TRIGGER ins_sum BEFORE INSERT ON account
    ->        FOR EACH ROW SET @sum = @sum + NEW.amount;
Query OK, 0 rows affected (0.06 sec)
Запрос CREATE TRIGGER создает триггер ins_sum, связанный с таблицей account. Это также включает пункты, которые определяют время действия, инициирующее событие, и что сделать, когда триггер активируется:

Чтобы использовать триггер, установите переменную сумматора в ноль, выполните INSERT и посмотрите новое значение переменной:

mysql> SET @sum = 0;
mysql> INSERT INTO account VALUES(137,14.98),(141,1937.50),(97,-100.00);
mysql> SELECT @sum AS 'Total amount inserted';
+-----------------------+
| Total amount inserted |
+-----------------------+
| 1852.48               |
+-----------------------+
В этом случае значение @sum после INSERT 14.98 + 1937.50 - 100 или 1852.48.

Чтобы разрушить триггер, используйте DROP TRIGGER. Вы должны определить имя схемы, если триггер не находится в схеме по умолчанию:

mysql> DROP TRIGGER test.ins_sum;
Если Вы удаляете таблицу, любые триггеры для таблицы также удалятся.

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

Возможно определить много триггеров для данной таблицы, у которых есть то же самое событие и время действия. Например, Вы можете иметь два триггера BEFORE UPDATE для таблицы. По умолчанию триггеры, у которых есть то же самое событие и время действия, активируются в порядке, в котором они создавались. Чтобы затронуть порядок, определите пункт после FOR EACH ROW, который указывает FOLLOWS или PRECEDES и название существующего триггера, у которого также есть то же самое событие и время действия. С PRECEDES новый триггер активируется после существующего.

Например, следующее определение задает другой триггер BEFORE INSERT для таблицы account:

mysql> CREATE TRIGGER ins_transaction BEFORE INSERT ON account
    -> FOR EACH ROW PRECEDES ins_sum
    -> SET
    -> @deposits = @deposits + IF(NEW.amount>0,NEW.amount,0),
    -> @withdrawals = @withdrawals + IF(NEW.amount<0,-NEW.amount,0);
Query OK, 0 rows affected (0.02 sec)
Этот триггер, ins_transaction, подобен ins_sum, но накапливает deposits и withdrawals порознь. У этого есть предложение PRECEDES, которое заставляет его активироваться прежде ins_sum: без того пункта триггер активировался бы после ins_sum, так как создан позднее ins_sum.

В пределах тела ключевые слова OLD и NEW позволяют Вам доступ к столбцам в строках, затронутых триггером. OLD и NEW расширения MySQL для триггеров, они не являются чувствительными к регистру.

В триггере INSERT может использоваться только NEW.col_name: нет никакой старой строки. В триггере DELETE нет никакой новой строки, поэтому может использоваться только OLD.col_name. В триггере UPDATE можно применить OLD.col_name, чтобы обратиться к столбцам строки прежде, чем они будут обновлены, и NEW.col_name, чтобы обратиться после того, как это обновлено.

Столбец, названный OLD, только для чтения. Вы можете обратиться к нему (если Вы имеете привилегию SELECT), но не изменить. Вы можете обратиться к столбцу, названному NEW, если Вы имеете привилегию SELECT для него. В триггере BEFORE Вы можете также изменить его значение с SET NEW.col_name = value , если Вы имеете привилегию UPDATE для него. Это означает, что Вы можете использовать триггер, чтобы изменить значения, которые будут вставляться в новую строку или использоваться, чтобы обновить строку. Такой запрос SET не имеет никакого эффекта в триггере AFTER, потому что изменение строки уже произойдет.

В триггере BEFORE значение NEW для столбца AUTO_INCREMENT 0, а не порядковый номер, который произведен автоматически, когда новая строка фактически вставлена.

При использовании BEGIN ... END Вы можете определить триггер, который выполняет много запросов. В пределах блока BEGIN Вы также можете использовать другой синтаксис, который разрешен в пределах сохраненных подпрограмм, например, условные предложения и циклы. Однако, так же, как для сохраненных подпрограмм, если Вы используете mysql, чтобы определить триггер, который выполняет много запросов, необходимо пересмотреть разделитель запроса так, чтобы Вы могли использовать ; в пределах определения триггера. Следующий пример иллюстрирует эти тезисы. Это определяет триггер UPDATE, который проверяет новое значение, которое будет использоваться для того, чтобы обновить каждую строку, и изменяет значение, чтобы оно было в пределах диапазона от 0 до 100. Это должен быть триггер BEFORE, потому что значение должно быть проверено прежде, чем оно будет использоваться, чтобы обновить строку:

mysql> delimiter //
mysql> CREATE TRIGGER upd_check BEFORE UPDATE ON account FOR EACH ROW
    -> BEGIN
    ->   IF NEW.amount < 0 THEN
    ->      SET NEW.amount = 0;
    ->   ELSEIF NEW.amount > 100 THEN
    ->      SET NEW.amount = 100;
    ->   END IF;
    -> END;//
mysql> delimiter ;
Может быть легче определить хранимую процедуру отдельно и затем вызвать ее из триггера, используя простой CALL . Это также выгодно, если Вы хотите выполнить тот же самый код из нескольких триггеров.

Есть ограничения на то, что может появиться в запросах, которые триггер выполняет когда активирован:

MySQL обрабатывает ошибки в триггере следующим образом:

Триггеры могут содержать прямые ссылки на таблицы по имени, такие как триггер testref, показанный в этом примере:

CREATE TABLE test1(a1 INT);
CREATE TABLE test2(a2 INT);
CREATE TABLE test3(a3 INT NOT NULL AUTO_INCREMENT PRIMARY KEY);
CREATE TABLE test4(a4 INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
                   b4 INT DEFAULT 0);
delimiter |
CREATE TRIGGER testref BEFORE INSERT ON test1 FOR EACH ROW
BEGIN
   INSERT INTO test2 SET a2 = NEW.a1;
   DELETE FROM test3 WHERE a3 = NEW.a1;
   UPDATE test4 SET b4 = b4 + 1 WHERE a4 = NEW.a1;
END;
|
delimiter ;
INSERT INTO test3 (a3) VALUES
   (NULL), (NULL), (NULL), (NULL), (NULL),
   (NULL), (NULL), (NULL), (NULL), (NULL);
INSERT INTO test4 (a4) VALUES
   (0), (0), (0), (0), (0), (0), (0), (0), (0), (0);
Предположите, что Вы вставляете следующие значения в таблицу test1 как показано здесь:
mysql> INSERT INTO test1 VALUES
    ->        (1), (3), (1), (7), (1), (8), (4), (4);
Query OK, 8 rows affected (0.01 sec)
Records: 8  Duplicates: 0  Warnings: 0
В результате эти четыре таблицы содержат следующие данные:
mysql> SELECT * FROM test1;
+----+
| a1 |
+----+
| 1  |
| 3  |
| 1  |
| 7  |
| 1  |
| 8  |
| 4  |
| 4  |
+----+
8 rows in set (0.00 sec)

mysql> SELECT * FROM test2;
+----+
| a2 |
+----+
| 1  |
| 3  |
| 1  |
| 7  |
| 1  |
| 8  |
| 4  |
| 4  |
+----+
8 rows in set (0.00 sec)

mysql> SELECT * FROM test3;
+----+
| a3 |
+----+
|  2 |
|  5 |
|  6 |
|  9 |
| 10 |
+----+
5 rows in set (0.00 sec)

mysql> SELECT * FROM test4;
+----+----+
| a4 | b4 |
+----+----+
|  1 | 3  |
|  2 | 0  |
|  3 | 1  |
|  4 | 2  |
|  5 | 0  |
|  6 | 0  |
|  7 | 1  |
|  8 | 1  |
|  9 | 0  |
| 10 | 0  |
+----+----+
10 rows in set (0.00 sec)

21.3.2. Метаданные триггеров

Метаданные триггеров могут быть получены следующим образом:

21.4. Использование планировщика событий

MySQL Event Scheduler управляет планированием и выполнением событий, то есть, задач, которые работают согласно графику. Следующее обсуждение покрывает планировщик событий.

Сохраненные подпрограммы требуют таблицы event в базе данных mysql. Эта таблица составлена во время процессов установки MySQL 8.0. Если Вы обновляетесь до MySQL 8.0 от более ранней версии, убедитесь, что обновили свои таблицы, чтобы удостовериться, что таблица event существует. См. раздел 5.4.5.

21.4.1. Краткий обзор планировщика событий

События MySQL это задачи, которые работают согласно графику. Поэтому мы иногда именуем их намеченные события. Когда Вы создаете событие, Вы создаете названный объект базы данных, содержащий один или более запросов SQL, которые будут выполнены в одном или более равных интервалах, начинаясь и заканчиваясь в определенную дату и время. Концептуально это подобно идее Unix crontab (также известный как cron job) или Windows Task Scheduler.

Запланированные задачи этого типа также иногда известны как временные триггеры, подразумевая, что они объекты, которые вызваны течением времени. В то время как это чрезвычайно правильно, мы предпочитаем использовать термин события, чтобы избежать беспорядка с триггерами. События не должны быть перепутаны с ними. Принимая во внимание, что триггер это объект базы данных, запрос которого выполнен в ответ на определенный тип ситуации, которая происходит в данной таблице, запланированное событие это объект, запрос которого выполнен в ответ на приход требуемого интервала времени.

В то время как нет никакого предоставления в стандарте SQL для планирования событий, в других системах базы данных есть прецеденты, и Вы можете заметить некоторые общие черты между этим выполнением и найденным в сервере MySQL.

У событий MySQL есть следующие основные функции и свойства:

21.4.2. Конфигурация планировщика событий

События запущены потоком планировщика, когда мы обращаемся к планировщику событий, мы фактически обращаемся к этому потоку. Работая, поток планировщика событий и его текущее состояние могут быть замечены пользователями, имеющими привилегию PROCESS в выводе SHOW PROCESSLIST, как показано далее.

Глобальная переменная event_scheduler определяет, включен ли планировщик событий. У этого есть одно из 3 значений, которые затрагивают событие, как описано здесь:

Если состояние планировщика событий не было установлено в DISABLED, event_scheduler может быть переключена между ON и OFF (с использованием SET). Также возможно использовать 0 для OFF и 1 для ON, устанавливая эту переменную. Таким образом, любой из следующих 4 запросов может использоваться в клиенте mysql, чтобы включить планировщик событий:

SET GLOBAL event_scheduler = ON;
SET @@global.event_scheduler = ON;
SET GLOBAL event_scheduler = 1;
SET @@global.event_scheduler = 1;
Точно так же любой из этих 4 запросов может использоваться, чтобы выключить планировщик событий:
SET GLOBAL event_scheduler = OFF;
SET @@global.event_scheduler = OFF;
SET GLOBAL event_scheduler = 0;
SET @@global.event_scheduler = 0;
Хотя ON и OFF имеют числовые эквиваленты, значение, выведенное на экран для event_scheduler через SELECT или SHOW VARIABLES всегда OFF, ON или DISABLED. DISABLED не имеет никакого числового эквивалента . Поэтому ON и OFF обычно предпочитаются 1 и 0, устанавливая эту переменную.

Заметьте, что попытка установить event_scheduler не определяя это как глобальную переменную вызывает ошибку:

mysql< SET @@event_scheduler = OFF;
ERROR 1229 (HY000): Variable 'event_scheduler' is a GLOBAL
variable and should be set with SET GLOBAL

Возможно установить планировщик событий в DISABLED только при запуске сервера. Если event_scheduler ON или OFF, Вы не можете установить это в DISABLED во время выполнения. Кроме того, если планировщик событий установлен в DISABLED при запуске Вы не можете изменить значение event_scheduler во время выполнения.

Чтобы отключить планировщик событий, используйте один из следующих двух методов:

Чтобы включить планировщик событий, перезапустите сервер без параметра командной строки --event-scheduler=DISABLED или после удаления (или комментирования) строки в конфигурационном файле сервера, содержащей event-scheduler=DISABLED. Альтернативно, Вы можете использовать ON (или 1) или OFF (или 0 ) вместо DISABLED, запуская сервер.

Вы можете сделать запрос манипуляции событий, когда event_scheduler DISABLED. Никакие предупреждения или ошибки не произведены в таких случаях (при условии, что запрос самостоятельно допустим). Однако, запланированные события не могут выполниться, пока эта переменная не установлена в ON (или 1). Как только это было сделано, поток планировщика событий запускает все события, условия планирования которых удовлетворены.

Запуск сервера MySQL с опцией --skip-grant-tables установит event_scheduler в DISABLED, переопределяя любое другое значение в командной строке или в файле my.cnf (или my.ini).

Для запросов SQL, используемых, чтобы создать, изменить и удалить события см. раздел 21.4.3.

MySQL обеспечивает таблицу EVENTS в базе данных INFORMATION_SCHEMA. Эта таблица может быть запрошена, чтобы получить информацию о запланированных событиях, которые были определены на сервере. См. разделы 21.4.4 и 22.7.

21.4.3. Синтаксис событий

MySQL обеспечивает несколько запросов SQL для того, чтобы они работали с запланированными событиями:

21.4.4. Метаданные событий

Метаданные о событиях могут быть получены следующим образом:

Представление времени планировщика событий

У каждого сеанса в MySQL есть часовой пояс сеанса (STZ). Это сеансовая переменная time_zone, которая инициализирована от глобального значения сервера time_zone, когда сеанс начинается, но может быть изменена во время сеанса.

Часовой пояс сеанса, который актуален, когда выполняется CREATE EVENT или ALTER EVENT, используется, чтобы интерпретировать времена, определенные при создании события. Это становится часовым поясом событий (ETZ), то есть, часовой пояс, который используется для планирования событий и является в действительности часовым поясом, в котором выполняется событие.

Для представления информации о событии в таблице mysql.event времена execute_at, starts и ends преобразованы в UTC и сохранены наряду с часовым поясом события. Это позволяет выполнению событий продолжиться как определено независимо от любых последующих изменений часового пояса сервера или эффектов летнего времени. Время last_executed также сохранено в UTC.

Если Вы выбираете информацию из mysql.event, времена получены как значения UTC. Эти времена могут также быть получены, выбирая из INFORMATION_SCHEMA.EVENTS или через SHOW EVENTS, но о них сообщают как о значениях ETZ. Другие времена, доступные из этих источников, указывают, когда событие создавалоси или последний раз изменилось, они выведены на экран как значения STZ. Следующая таблица суммирует представление времен событий.

Значение mysql.event INFORMATION_SCHEMA.EVENTS SHOW EVENTS
Execute atUTCETZETZ
StartsUTCETZETZ
EndsUTCETZETZ
Last executedUTCETZn/a
CreatedSTZSTZn/a
Last alteredSTZSTZn/a

21.4.5. Состояние планировщика событий

Планировщик событий пишет информацию о выполнении события, которое заканчивается с ошибкой или предупреждением в журнал ошибок сервера MySQL. См. раздел 21.4.6.

Чтобы получить информацию о статусе планировщика событий для отладки и поиска неисправностей, выполните mysqladmin debug (см. раздел 5.5.2). После выполнения этой команды журнал ошибок сервера содержит вывод, касающийся планировщика событий, подобный тому, что показывают здесь:

Events status:
LLA = Last Locked At  LUA = Last Unlocked At
WOC = Waiting On Condition  DL = Data Locked

Event scheduler status:
State: INITIALIZED
Thread id  : 0
LLA: init_scheduler:313
LUA: init_scheduler:318
WOC: NO
Workers: 0
Executed   : 0
Data locked: NO

Event queue status:
Element count   : 1
Data locked : NO
Attempting lock : NO
LLA   : init_queue:148
LUA   : init_queue:168
WOC   : NO
Next activation : 0000-00-00 00:00:00
В запросах, которые происходят как часть событий, запущенных планировщиком событий, сообщения диагностики (не только ошибки, а также и предупреждения) написаны в журнал ошибок и в Windows в журнал событий приложения. Для часто запускаемых событий это может привести ко многим зарегистрированным сообщениям. Например, для запроса SELECT ... INTO var_list, если запрос не возвращает строк, предупреждения с кодом ошибки 1329, происходят (No data), и переменные значения остаются неизменными. Если запрос возвращает много строк, происходит ошибка 1172 (Result consisted of more than one row ). Для любого условия Вы можете избежать регистрации предупреждений, объявляя обработчик условия, см. раздел 14.6.7.2. Для запросов, которые могут получить много строк, другая стратегия состоит в том, чтобы использовать LIMIT 1, чтобы ограничить набор результатов единственной строкой.

21.4.6. Привилегии планировщика и событий MySQL

Чтобы включить или отключить выполнение запланированных событий, необходимо установить значение глобальной переменной event_scheduler. Это требует привилегии SUPER .

Привилегия EVENT управляет созданием, модификацией и удалением событий. Эта привилегия может дароваться, используя GRANT. Например, этот запрос GRANT предоставляет привилегию EVENT для схемы myschema пользователю jon@ghidora:

GRANT EVENT ON myschema.* TO jon@ghidora;
Мы предполагаем, что эта учетная запись пользователя уже существует.

Чтобы предоставить тому же самому пользователю привилегию EVENT на всех схемах, используйте следующее запрос:

GRANT EVENT ON *.* TO jon@ghidora;
Привилегия EVENT имеет глобальный контекст или на уровне схемы. Поэтому попытка предоставить это на единственной таблице приводит к ошибке как показано:
mysql> GRANT EVENT ON myschema.mytable TO jon@ghidora;
ERROR 1144 (42000): Illegal GRANT/REVOKE command; please
consult the manual to see which privileges can be used
Важно понять, что событие запущено с привилегиями его создателя, и что оно не может выполнить действия, для которых у его автора нет необходимых привилегий. Например, предположите, что jon@ghidora имеет привилегию EVENT для myschema. Предположите также, что этот пользователь имеет привилегию SELECT для myschema, но никаких других привилегий для этой схемы. Для jon@ghidora возможно создать новое событие:
CREATE EVENT e_store_ts ON SCHEDULE EVERY 10 SECOND DO
       INSERT INTO myschema.mytable VALUES (UNIX_TIMESTAMP());
Пользователь ждет в течение приблизительно одной минуты, и затем выполняет запрос SELECT * FROM mytable;, ожидая увидеть несколько новых строк в таблице. Вместо этого таблица пуста. Так как пользователь не имеет привилегии INSERT для рассматриваемой таблицы, событие не имеет никакого эффекта.

Если Вы просматриваете журнал ошибок MySQL (hostname.err), Вы можете видеть, что событие выполняется, но действие, которое оно пытается выполнить, терпит неудачу:

2013-09-24T12:41:31.261992Z 25 [ERROR] Event Scheduler:
[jon@ghidora][cookbook.e_store_ts] INSERT command denied to user
'jon'@'ghidora' for table 'mytable'
2013-09-24T12:41:31.262022Z 25 [Note] Event Scheduler:
[jon@ghidora].[myschema.e_store_ts] event execution failed.
2013-09-24T12:41:41.271796Z 26 [ERROR] Event Scheduler:
[jon@ghidora][cookbook.e_store_ts] INSERT command denied to user
'jon'@'ghidora' for table 'mytable'
2013-09-24T12:41:41.272761Z 26 [Note] Event Scheduler:
[jon@ghidora].[myschema.e_store_ts] event execution failed.
Так как у этого пользователя очень вероятно нет доступа к журналу ошибок, возможно проверить, допустим ли запрос, выполняя это непосредственно:
mysql> INSERT INTO myschema.mytable VALUES (UNIX_TIMESTAMP());
ERROR 1142 (42000): INSERT command denied to user
'jon'@'ghidora' for table 'mytable'

Просмотр таблицы INFORMATION_SCHEMA.EVENTS показывает, что e_store_ts существует и включен, но столбец LAST_EXECUTED NULL:

mysql> SELECT * FROM INFORMATION_SCHEMA.EVENTS
     >          WHERE EVENT_NAME='e_store_ts' AND
     >          EVENT_SCHEMA='myschema'\G
*************************** 1. row ***************************
   EVENT_CATALOG: NULL
EVENT_SCHEMA: myschema
EVENT_NAME: e_store_ts
 DEFINER: jon@ghidora
EVENT_BODY: SQL
EVENT_DEFINITION: INSERT INTO myschema.mytable VALUES (UNIX_TIMESTAMP())
EVENT_TYPE: RECURRING
EXECUTE_AT: NULL
  INTERVAL_VALUE: 5
  INTERVAL_FIELD: SECOND
SQL_MODE: NULL
STARTS: 0000-00-00 00:00:00
  ENDS: 0000-00-00 00:00:00
STATUS: ENABLED
   ON_COMPLETION: NOT PRESERVE
 CREATED: 2006-02-09 22:36:06
LAST_ALTERED: 2006-02-09 22:36:06
   LAST_EXECUTED: NULL
   EVENT_COMMENT:
1 row in set (0.00 sec)
Отменить привилегию EVENT можно через REVOKE. В этом примере привилегия EVENT на схеме myschema удалена из учетной записи пользователя jon@ghidora:
REVOKE EVENT ON myschema.* FROM jon@ghidora;

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

Событие не мигрирует или удаляется в результате переименования или удаления пользователя, который его создал.

Предположите что пользователю jon@ghidora были даны привилегии EVENT и INSERT на схеме myschema. Этот пользователь тогда создает следующий событие:

CREATE EVENT e_insert ON SCHEDULE EVERY 7 SECOND DO
       INSERT INTO myschema.mytable;
После того, как это событие было создано, root отменяет привилегию EVENT для jon@ghidora. Однако, e_insert продолжает выполняться, вставляя новую строку в mytable каждые семь секунд. То же самое было бы истиной, если root сделал любой из этих запросов:

Вы можете проверить, что это истина, исследуя таблицу mysql.event или INFORMATION_SCHEMA.EVENTS (см. раздел 22.7) до и после выполнения DROP USER или RENAME USER.

Определения событий сохранены в таблице mysql.event. Чтобы удалить событие, созданное другой учетной записью пользователя, MySQL root (или другой пользователь с необходимыми привилегиями) может удалить строки из этой таблицы. Например, чтобы удалить событие e_insert root может использовать следующий запрос:

DELETE FROM mysql.event WHERE db = 'myschema' AND
       definer = 'jon@ghidora' AND name = 'e_insert';
Очень важно соответствовать имени события, имени схемы базы данных и учетной записи пользователя, удаляя строки из mysql.event. Тот же самый пользователь может создать различные события с тем же самым именем в различных схемах.

Пользовательские привилегии EVENT сохранены в столбцах Event_priv таблиц mysql.user и mysql.db. В обоих случаях этот столбец содержит одно из значений 'Y' или 'N' (по умолчанию 'N'). mysql.user.Event_priv установлен в 'Y' для данного пользователя, только если у этого пользователя есть глобальная привилегия EVENT (то есть, если привилегия даровалась, используя GRANT EVENT ON *.*). Для привилегии EVENT на уровне схемы GRANT создает строку в mysql.db и устанавливает столбец Db в имя схемы, столбец User к имени пользователя, а столбец Event_priv в 'Y'. Никогда не должно быть никакой потребности управлять этими таблицами непосредственно, запросы GRANT EVENT и REVOKE EVENT выполняют необходимые операции на них.

Пять переменных состояния предоставляют счетчики в связанных с событиями операциях (но НЕ запросов, выполненных событиями, см. раздел C.1). Это:

Вы можете рассмотреть текущие значения для всех них, выполняя запрос SHOW STATUS LIKE '%event%';.

21.5. Использование представлений

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

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

21.5.1. Синтаксис представления

Запрос CREATE VIEW создает новое представление (см. раздел 14.1.18). Чтобы изменить определение представления или удалить представление, надо использовать ALTER VIEW (см. раздел 14.1.8) или DROP VIEW (см. раздел 14.1.28).

Представление может быть создано из многих видов запросов SELECT. Это может сослаться на базовые таблицы или другие представления. Это может использовать объединения, UNION и подзапросы. SELECT не должен даже обратиться ни к каким таблицам. Следующий пример определяет представление, которое выбирает два столбца из другой таблицы, так же как выражение, вычисленное от тех столбцов:

mysql> CREATE TABLE t (qty INT, price INT);
mysql> INSERT INTO t VALUES(3, 50), (5, 60);
mysql> CREATE VIEW v AS SELECT qty, price, qty*price AS value FROM t;
mysql> SELECT * FROM v;
+------+-------+-------+
| qty  | price | value |
+------+-------+-------+
| 3    | 50    |   150 |
| 5    | 60    |   300 |
+------+-------+-------+

mysql> SELECT * FROM v WHERE qty = 5;
+------+-------+-------+
| qty  | price | value |
+------+-------+-------+
| 5    | 60    |   300 |
+------+-------+-------+

21.5.2. Алгоритмы обработки представления

Дополнительное предложение ALGORITHM для CREATE VIEW или ALTER VIEW это расширение MySQL для SQL. Это затрагивает, как MySQL обрабатывает представление. ALGORITHM берет три значения: MERGE, TEMPTABLE или UNDEFINED.

Причина определить TEMPTABLE явно: блокировки могут быть сняты на основных таблицах после того, как временная таблица была составлена и прежде, чем она будет использоваться, чтобы закончить обрабатывать запрос. Это могло бы привести к более быстрому снятию блокировки, чем алгоритм MERGE так, чтобы другие клиенты, которые используют представление, не были заблокированы надолго.

Алгоритм представления может быть UNDEFINED по трем причинам:

Как упомянуто ранее, MERGE обработан, сливая соответствующие части определения представления в запрос, которое относится к представлению. Следующие примеры кратко иллюстрируют как работает алгоритм MERGE. Примеры предполагают, что есть представление v_merge, у которого есть это определение:

CREATE ALGORITHM = MERGE VIEW v_merge (vc1, vc2) AS
       SELECT c1, c2 FROM t WHERE c3 > 100;
Пример 1: Предположите, что мы делаем этот запрос:
SELECT * FROM v_merge;
MySQL обрабатывает запрос следующим образом:

Получается запрос, который будет выполнен:

SELECT c1, c2 FROM t WHERE c3 > 100;
Пример 2: Предположите, что мы делаем этот запрос:
SELECT * FROM v_merge WHERE vc1 < 100;
Это запрос обработан так же, как предыдущий, за исключением того, что vc1 < 100 становится c1 < 100 и предложение WHERE добавлено к WHERE запроса, используя AND (и круглые скобки добавлены, чтобы удостовериться, что части предложения выполнены с правильным приоритетом). Получается запрос, который будет выполнен:
SELECT c1, c2 FROM t WHERE (c3 > 100) AND (c1 < 100);
Эффективно, у запроса, который будет выполнен, есть WHERE этой формы:
WHERE (select WHERE) AND (view WHERE)
Если алгоритм MERGE не может использоваться, временная таблица должна использоваться вместо этого. MERGE не может использоваться, если представление содержит какие-либо конструкции в следующем списке. Эти конструкции также предотвращают слияние полученных таблиц (см. раздел 9.2.1.18.3).

21.5.3. Обновляемые и вставляемые представления

Некоторые представления обновляемые и ссылки на них могут использоваться, чтобы определить таблицы, которые будут обновлены в запросах изменения данных. Таким образом, Вы можете использовать их в таких запросах, как UPDATE, DELETE или INSERT, чтобы обновить содержание основной таблицы. Полученные таблицы могут также быть определены в таблице запроса UPDATE и DELETE, но могут использоваться только для того, чтобы считать данные, чтобы определить строки, которые будут обновлены или удалены. Вообще, ссылки представления должны быть обновляемыми, означая, что они могут быть слиты и не осуществлены. У сложных представлений есть более сложные правила.

Для представления, чтобы быть обновлемым, должны быть непосредственные отношения между строками в представлении и строками в основной таблице. Есть также определенные другие конструкции, которые делают представление необновлемым. Чтобы быть более определенным, представление не обновляемое, если это содержит какое-либо следующие элементы:

Произведенный столбец в представлении считают обновляемым, потому что возможно назначить на него. Однако, если такой столбец обновлен явно, единственное разрешенное значение DEFAULT.

Для многотабличного представления иногда возможно быть обновляемым, предполагая, что это может быть обработано с алгоритмом MERGE. Для того, чтобы это работало, представление должно использовать внутреннее соединение (не внешнее соединение или UNION ). Кроме того, только единственная таблица в определении представления может быть обновлена, таким образом, предложение SET должно назвать только столбцы одной из таблиц в представлении. Представления, которые используют UNION ALL не разрешены даже при том, что они могли бы быть теоретически обновляемыми.

Относительно вставляемых (то есть обновляемых запросом INSERT) представлений. Представление вставляемое, если оно обновляемое, а также удовлетворяет эти дополнительные требования для столбцов представления:

MySQL устанавливает флаг, названный обновляемым представлением, во время CREATE VIEW. Флаг установлен в to YES (true), если UPDATE и DELETE (и подобные операции), являются законными для представления. Иначе, флаг установлен в NO (false). Столбец IS_UPDATABLE в INFORMATION_SCHEMA.VIEWS выводит на экран состояние этого флага. Это означает, что сервер всегда знает, является ли представление обновляемым.

Если представление не обновляемое, такие запросы, как UPDATE, DELETE и INSERT незаконны и отклонены. Отметьте, что, даже если представление обновляемое, невозможно вставить в него, как описано в другом месте в этом разделе.

Обновляемость представлений может быть затронут значением переменной updatable_views_with_limit. См. раздел 6.1.5.

Для следующего обсуждения, предположите, что эти таблицы и представления существуют:

CREATE TABLE t1 (x INTEGER);
CREATE TABLE t2 (c INTEGER);
CREATE VIEW vmat AS SELECT SUM(x) AS s FROM t1;
CREATE VIEW vup AS SELECT * FROM t2;
CREATE VIEW vjoin AS SELECT * FROM vmat JOIN vup ON vmat.s=vup.c;
Запросы INSERT, UPDATE и DELETE разрешены следующим образом:

Более раннее обсуждение в этом разделе указало, что представление не вставляемое, если не все столбцы простые ссылки столбца (например, если это содержит столбцы, которые являются выражениями или сложными выражениями). Хотя такое представление не вставляемое, это может быть обновляемым, если Вы обновляете только столбцы, которые не являются выражениями. Рассмотрите это представление:

CREATE VIEW v AS SELECT col1, 1 AS col2 FROM t;
Это представление не вставляемое, потому что col2 выражение. Но оно обновляемое, если обновление не пытается обновить col2. Это обновление допустимо:
UPDATE v SET col1 = 0;
Это обновление не допустимо, потому что оно пытается обновить столбец выражения:
UPDATE v SET col2 = 0;
Если таблица содержит столбец AUTO_INCREMENT, вставка во вставляемое представление на таблице, которая не включает столбец AUTO_INCREMENT, не изменяет значение LAST_INSERT_ID() , потому что побочные эффекты вставки значений по умолчанию в столбцы, не являющиеся частью представления, не должны быть видимыми.

21.5.4. Предложение WITH CHECK OPTION

Предложение WITH CHECK OPTION может быть дано для обновляемого представления, чтобы предотвратить вставку строк, для которых WHERE в select_statement не true. Это предотвращает обновления строк, для которых WHERE true, но обновление заставило бы это быть не истиной (другими словами, это препятствует тому, чтобы видимые строки были обновлены к невидимым строкам).

В предложении WITH CHECK OPTION для обновляемого представления ключевые слова LOCAL и CASCADED определяют контекст тестирования проверки, когда представление определено с точки зрения другого представления. Когда никакое ключевое слово не задано, значение по умолчанию CASCADED.

Тестирование WITH CHECK OPTION следует правилам:

Рассмотрите определения для следующей таблицы и набора представлений:

CREATE TABLE t1 (a INT);
CREATE VIEW v1 AS SELECT * FROM t1 WHERE a < 2 WITH CHECK OPTION;
CREATE VIEW v2 AS SELECT * FROM v1 WHERE a > 0 WITH LOCAL CHECK OPTION;
CREATE VIEW v3 AS SELECT * FROM v1 WHERE a > 0 WITH CASCADED CHECK OPTION;
Здесь представления v2 и v3 определены с точки зрения другого представления v1.

Вставки для v2 проверены по опции LOCAL, затем проверка переходит к v1 и правила применены снова. Правила для v1 вызовут отказ проверки. Проверка на v3 также терпит неудачу:

mysql> INSERT INTO v2 VALUES (2);
ERROR 1369 (HY000): CHECK OPTION failed 'test.v2'
mysql> INSERT INTO v3 VALUES (2);
ERROR 1369 (HY000): CHECK OPTION failed 'test.v3'

21.5.5. Метаданные о представлении

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

21.6. Управление доступом для сохраненных программ и представлений

Сохраненные программы и представления определены до использования и выполняются в пределах контекста безопасности, который определяет их привилегии. Этими привилегиями управляет их атрибут DEFINER и, если есть, характеристика SQL SECURITY.

У всех сохраненных программ (процедуры, функции, триггеры и события) и представлений может быть атрибут DEFINER, который называет учетную запись MySQL. Если признак DEFINER опущен в сохраненной программе или определении представления, учетная запись по умолчанию это пользователь, который создает объект.

Кроме того, у сохраненных подпрограмм (процедуры и функции) и представлений может быть характеристика SQL SECURITY со значением DEFINER или INVOKER, чтобы определить, выполняется ли объект в контексте создателя или вызывающего. Если SQL SECURITY нет, значение по умолчанию контекст создателя.

Триггеры и события не имеют SQL SECURITY и всегда выполняются в контексте создателя. Сервер вызывает эти объекты автоматически по мере необходимости, таким образом нет никакого пользователя вызова.

Контексты безопасности отличаются следующим образом:

Рассмотрите следующую хранимую процедуру:

CREATE DEFINER = 'admin'@'localhost' PROCEDURE p1()
SQL SECURITY DEFINER
BEGIN
  UPDATE t1 SET counter = counter + 1;
END;
Любой пользователь, который имеет привилегию EXECUTE для p1 может вызвать это с CALL . Однако, когда p1 выполняется, это работает в контексте безопасности DEFINER и таким образом выполняется с привилегиями 'admin'@'localhost', учетной записм, названной в атрибуте DEFINER. Эта учетная запись должна иметь привилегию EXECUTE для p1 так же как UPDATE для t1. Иначе процедура терпит неудачу.

Теперь рассмотрите эту хранимую процедуру, которая идентична p1 за исключением того, что характеристика SQL SECURITY INVOKER:

CREATE DEFINER = 'admin'@'localhost' PROCEDURE p2()
SQL SECURITY INVOKER
BEGIN
  UPDATE t1 SET counter = counter + 1;
END;
p2, в отличие от p1, выполняется в контексте безопасности INVOKER. Атрибут DEFINER не важен и p2 выполняется с привилегиями пользователя вызова. p2 терпит неудачу, если вызывающему недостает привилегии EXECUTE для p2 или UPDATE для таблицы с именем t1.

MySQL использует следующие правила, чтобы управлять тем, какие учетные записи пользователь может определить в атрибуте DEFINER объекта:

Чтобы минимизировать потенциал риска для сохраненной программы и создания представления, следуйте этим советам:

21.7. Двоичное журналирование сохраненных программ

Двоичный журнал содержит информацию о запросах SQL, которые изменяют содержание базы данных. Эта информация хранится в форме событий, которые описывают модификации. У двоичного журнала есть две важных цели:

Однако, есть определенные двоичные проблемы журналирования, которые применяются относительно сохраненных программ (хранимые процедуры и функции, триггеры и события), если журналирование происходит на уровне запроса:

Этот раздел описывает, как MySQL обрабатывает двоичное журналирование для сохраненных программ. Это заявляет текущие положения, которые выполнение помещает в использование сохраненных программ, и что Вы можете сделать, чтобы избежать проблем. Это также обеспечивает дополнительную информацию о причинах этих условий.

Вообще, проблемы описанные здесь, бывают, когда двоичное журналирование происходит на уровне запроса SQL. Если Вы используете основанное на строке двоичное журналирование, журнал содержит изменения, произведенные в отдельных строках в результате выполнения запросов SQL. Когда подпрограммы или триггеры выполняются, зарегистрированы изменения строки, а не запросы, которые производят изменения. Для хранимых процедур это означает, что запрос CALL не зарегистрирован. Для сохраненных функций изменения строки, произведенные в пределах функции, зарегистрированы, а не вызов функции. Для триггеров зарегистрированы изменения строки, произведенные триггером. На ведомой стороне замечены только изменения строки, а не сохраненный вызов программы. Для общей информации об основанном на строке журналировании см. раздел 19.2.1.

Если не отмечено иное, замечания здесь предполагают, что Вы включили двоичное журналирование, запуская сервер с опцией --log-bin (см. раздел 6.4.4). Если двоичный журнал не включен, репликация невозможна, и при этом двоичный журнал недоступен для восстановления данных.

Условия использования сохраненных функций в MySQL могут быть получены в итоге следующим образом. Эти условия не относятся к хранимым процедурам или событиям планировщика событий и не применяются, если двоичное журналирование не включено.

Триггеры подобны сохраненным функциям, таким образом, предыдущие замечания относительно функций также относятся к триггерам со следующим исключением: CREATE TRIGGER не имеет дополнительной характеристики DETERMINISTIC, таким образом, триггеры, как предполагается, всегда детерминированы. Однако, это предположение могло бы в некоторых случаях быть недопустимым. Например, функция UUID() недетерминирована (и не копируется). Вы должны быть осторожными относительно использования таких функций в триггерах.

Триггеры могут обновить таблицы, таким образом, сообщения об ошибках, подобные тем для сохраненных функций, происходят с CREATE TRIGGER, если у Вас нет необходимых привилегий. На ведомой стороне сервер использует атрибут DEFINER триггера, чтобы определить, какой пользователь является создателем триггера.

Остальная часть этого раздела обеспечивает дополнительные детали о выполнении журналирования и его значениях. Это обсуждение применяется только для основанного на запросе журналирования, а не для основанного на строке журналирования, за исключением первого элемента: CREATE и DROP зарегистрированы как запросы независимо от режима журналирования.