Ошибка CREATE FUNCTION "Эта функция не имеет DETERMINISTIC, NO SQL или READS SQL DATA"

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

CREATE FUNCTION NextOrderNumber() RETURNS INTEGER UNSIGNED NOT DETERMINISTIC
BEGIN
  DECLARE number INTEGER UNSIGNED;
  UPDATE Settings SET IntegerValue=LAST_INSERT_ID(IntegerValue+1) WHERE KeyName='NextOrderNumber';
  SET number=LAST_INSERT_ID();
  return number;
END

Примечание. Не критикуйте эту функцию, я знаю, что она имеет недостатки только для иллюстрации.

Мы используем эту функцию следующим образом:

INSERT INTO Orders(OrderNumber, ...)
SELECT NextOrderNumber(), ...

Когда двоичный журнал включен, CREATE FUNCTION дает эту ошибку:

Эта функция не имеет DETERMINISTIC, NO SQL или READS SQL ДАННЫЕ в декларации и двоичном регистрация включена (возможно, вы захотите использовать менее безопасные log_bin_trust_function_creators переменная)

Независимо от того, какой binlog_format установлен, действительно ли существует проблема с указанной выше функцией? Согласно моему чтению соответствующей страницы MySQL, я не вижу никакой причины, по которой эта функция несовместима с репликацией, с уровнем ROW или STATEMENT двоичный журнал.

Если функция безопасна, установка глобального log_bin_trust_function_creators = 1 делает меня непростым. Я не хочу отключать эту проверку для всех функций, только этого. Могу ли я вместо этого просто добавить функцию NO SQL для подавления предупреждения? Я попробовал, и это сработало. Это вызовет какие-либо проблемы?

Ответы

Ответ 1

У меня есть googled, и я здесь. Я нашел способ:

SET GLOBAL log_bin_trust_function_creators = 1;

Но будьте осторожны, это может быть опасно для восстановления данных или репликации...

Ответ 2

В соответствии с моим занижением это вызывает проблемы при восстановлении данных или репликации

Ссылка: http://dev.mysql.com/doc/refman/5.0/en/stored-programs-logging.html

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

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

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

например.

CREATE FUNCTION myfunc () RETURNS INT DETERMINISTIC
BEGIN
  INSERT INTO t (i) VALUES(1);
  RETURN 0;
END;

SELECT myfunc();

Если хранимая функция вызывается внутри оператора, такого как SELECT, который не изменяет данные, выполнение функции не записывается в двоичный журнал, даже если сама функция изменяет данные. Это поведение журнала может вызвать проблемы. Предположим, что функция myfunc() определена, как указано выше.

Ответ 3

Есть два способа исправить это:

Выполните следующие действия в консоли MySQL:

SET GLOBAL log_bin_trust_function_creators = 1;

Добавьте в конфигурационный файл mysql.ini следующее:

log_bin_trust_function_creators = 1

Параметр ослабляет проверку не детерминированных функций. Недетерминированные функции - это функции, которые изменяют данные (т.е. Имеют инструкции обновления, вставки или удаления). Для получения дополнительной информации см. Здесь.

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

Ответ 4

Подумайте о том, что нужно записать в двоичный журнал.

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

 0) Node 1 and Node 2 are in sync, NextOrderNumber=100
 1) Node 1 receives insert statement wrt order from customer A and assigns 
    order number 100, changes its NextOrderNumber to 101
 2) Node 1 writes the settings update to the log
 3) Node 1 writes the insert statement to the log
 4) Node 2 processes for customer B, asigns order number 100 and increments
 5) Node 2 writes the settings update from to the log
 6) Node 2 writes the insert statement to the log
 7) Nodes 2 reads settings update from the log @2 
         - Its NextOrderNumber is now 102
 8) Node 2 reads insert from log @3, tries to apply it but it fails 
         due to duplicate key
 9) Node 1 reads the update @5 - Its nextOrderNumber is also now 102
 10) Node1 reads insert from log @6 - 
         but this fails due to duplicate key

Теперь заказы 100 на 2 узлах относятся к разным данным, а порядка 101.

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

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

Ответ 5

Могу ли я вместо этого просто указывать функцию как NO SQL для подавления предупреждения? Я попробовал, и это сработало. Это вызовет какие-либо проблемы?

В соответствии с этим Документ Mysql:

Оценка характера функции основана на "честности" создателя: MySQL не проверяет, что функция, объявленная DETERMINISTIC, свободна от операторов, которые производят недетерминированные результаты.

Так это вам. Если вы уверены, что метод не вызовет никаких проблем...

Ответ 6

Выполните это непосредственно перед созданием функции:

SET @@global.log_bin_trust_function_creators = 1;

И добавьте MODIFIES SQL DATA в объявление.

Также... ну, вы попросили не комментировать саму функцию, но я предлагаю вам отказаться от переменной number и просто сделать RETURN LAST_INSERT_ID().