Ошибка СОЗДАНИЯ ФУНКЦИИ «Эта функция не имеет ДЕТЕРМИНИСТИЧЕСКОГО, НЕТ SQL или ЧТЕНИЯ ДАННЫХ SQL» - PullRequest
15 голосов
/ 07 февраля 2011

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

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 выдает эту ошибку:

Эта функция не имеет ДЕТЕРМИНИСТИЧЕСКИЙ, НЕТ SQL или ЧИТАЕТ SQL ДАННЫЕ в своей декларации и двоичные ведение журнала включено (вы могли бы хотите использовать менее безопасный log_bin_trust_function_creators переменная)

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

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

Ответы [ 7 ]

25 голосов
/ 08 апреля 2013

Я погуглил и вот я здесь. Я нашел способ:

SET GLOBAL log_bin_trust_function_creators = 1;

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

6 голосов
/ 23 мая 2013

Согласно моему занижению, это вызывает проблему при восстановлении или репликации данных

Ref: 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() определена, как указано выше.

1 голос
/ 22 сентября 2015

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

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

SET GLOBAL log_bin_trust_function_creators = 1;

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

log_bin_trust_function_creators = 1

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

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

0 голосов
/ 26 мая 2018

add READS SQL DATA, который объявляет, что это функция только для чтения:

CREATE FUNCTION NextOrderNumber() RETURNS INTEGER UNSIGNED NOT DETERMINISTIC
READS SQL DATA
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
0 голосов
/ 12 апреля 2016

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

В соответствии с этим Mysql doc :

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

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

0 голосов
/ 23 мая 2013

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

SET @@global.log_bin_trust_function_creators = 1;

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

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

0 голосов
/ 07 февраля 2011

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

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

 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.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...