#mysql #function #deterministic
Вопрос:
У нас есть большая система баз данных, в которой есть таблица статических значений, которые передаются внешним приложениям с помощью простого вызова функции. Цель состоит в том, чтобы обеспечить точное соответствие синхронизации между системой базы данных и серверной системой, чтобы обе системы могли ссылаться на одни и те же значения на основе пользовательских имен.
Реализация довольно проста. В таблице просто хранится текстовое имя системы и поля (VARCHAR64) вместе со значением (SMALLINT). По сути, это таблица поиска жестко закодированных значений, которыми мы делимся с командой разработчиков JavaScript, поэтому данные могут быть:
CUSTOMER_RECORD_STATE, ACTIVE, 1 CUSTOMER_RECORD_STATE, INACTIVE, 2 CUSTOMER_RECORD_STATE, DELETED, 3
Функция используется для поиска этих значений, как показано ниже.
SELECT tab.name_first FROM our_customers ourc WHERE ourc.record_state = get_gcs('CUSTOMER_RECORD_STATE','ACTIVE');
Вот код создания функции:
SET NAMES 'utf8mb4'; DELIMITER $ CREATE DEFINER = 'root'@'localhost' FUNCTION get_gcs (in_system varchar(128), in_field varchar(128), in_value varchar(128)) RETURNS smallint(6) DETERMINISTIC READS SQL DATA BEGIN DECLARE var_value smallint DEFAULT -1; DECLARE out_result_value smallint; DECLARE debug_definition json DEFAULT JSON_OBJECT('function', 'get_gcs'); DECLARE debug_details json DEFAULT JSON_OBJECT('source', debug_definition, 'parameters', JSON_OBJECT('in_system', in_system, 'in_field', in_field, 'in_value', in_value)); SELECT custom_value INTO var_value FROM global_custom_setting WHERE in_system = name_system AND in_field = name_field AND in_value = name_value; RETURN var_value; END $ DELIMITER ;
character_set_client: utf8mb4
сопоставление_соединение: utf8mb4_0900_ai_ci
Параметры сортировки базы данных: utf8mb4_unicode_ci
Система проста и хорошо работает; серверная команда постоянно вызывает эту функцию для извлечения этих «статических» значений, определенных в базе данных.
Проблема в том, что, хотя функция ДЕТЕРМИНИРОВАНА, она, по-видимому, значительно замедляет сложные вызовы SQL, в которые встроена функция. Я был удивлен этим, так как у меня сложилось впечатление, что ДЕТЕРМИНИРОВАННЫЕ функции будут обрабатываться по-другому; как если бы они были статическим значением (в вызове SQL).
Итак, вопросы:
- Является ли это лучшим подходом к совместному использованию статических значений между двумя платформами (базой данных и серверной частью)?
- Почему движок MySQL не рассматривает вызовы как ДЕТЕРМИНИРОВАННЫЕ и разрешает значение только один раз, а не с помощью того, что кажется с каждым итеративным вызовом?
Спасибо!
Комментарии:
1. Сколько строк внутри
var_value
и внутриglobal_custom_setting
?2. Пожалуйста, предоставьте
SHOW CREATE FUNCTION get_gcs
; Меня интересует сопоставление, назначенное во время создания. Также, какие параметры сортировки действуют во время вызова?3. Спасибо @RickJames. В таблице всего 745 строк. Я обновил ОПЕРАЦИЮ, чтобы ПОКАЗАТЬ ФУНКЦИЮ СОЗДАНИЯ. Независимо от этих настроек, является ли функция, предоставляющая доступ к табличным данным, наилучшим способом совместного использования статических «значений» с внешним приложением (например, Node.js Бэкэнд)?
4. Есть три вещи, которые нужно перечислить, такие как: character_set_client: utf8 параметры сортировки_соединение: utf8_general_ci Параметры сортировки базы данных: utf8mb4_unicode_520_ci
5. @RickJames обновлено.
Ответ №1:
Одна вещь, которую следует (и можно) исправить — UCASE(in_system) = UCASE(name_system)
Если оба столбца имеют одинаковую кодировку и параметры сортировки, UCASE
в этом нет необходимости. Кроме того, параметры сортировки ..._ci
должны быть «Без учета регистра».
Любой вызов функции скрывает столбцы от использования в любом индексе. Следовательно WHERE
, предложение в функции не может использовать индекс.
global_custom_setting
потребности INDEX(name_system, name_field, name_value)
. (Столбцы могут располагаться в любом порядке.)
По-видимому, FUNCTION
используется collation_connection: utf8mb4_0900_ai_ci
, что может отличаться от параметров сортировки соединения.
Предлагаю попробовать это после удаления UPPERs
и добавления INDEX
:
SET NAMES utf8mb4, COLLATE utf8mb4_0900_ai_ci; DROP FUNCTION get_gcs; DELIMITER $ ((recreate the function)) DELIMITER ; ((similarly for get_gss))
По-видимому, это относится к строкам в функции:
- Аргументы функции: collation_database
- Литеральные строки в функции: collation_connection
- Столбцы таблицы: сопоставление столбцов
Функции никогда не могут быть саргируемыми (насколько я знаю). Некоторые выражения включают «неявные» функции, такие как преобразование типов данных и несоответствие параметров сортировки.
Комментарии:
1. Спасибо, Рик! Этот код был написан почти 2 года назад, поэтому я совершенно забыл о ненужном использовании UCASE(). Удаление этого действительно ускорило процесс, но, насколько я понимаю, основная проблема заключается в том, что MySQL вызывает функцию для каждой строки в запросе по сравнению с одним вызовом, регистрирующим статическое значение. Например, если я заменю вызов функции жестко закодированным номером или даже переменной, это будет в 5-10 раз быстрее. Есть ли вообще какой — либо способ заставить MySQL использовать результат функции как единичный вызов, а не рекурсивный? Я думал, что ДЕТЕРМИНИЗМ сделает это, но, очевидно, нет…
2. @Floobinator — Я не разобрался в твоей
DETERMINISTIC
проблеме. Что касается вызовов функций-те, которые, очевидно, являются постоянными, оцениваются перед выполнением запроса; например,UPPER("abcd")
. Но те, которые не могут быть оценены заранее (например,UPPER(column)
), сталкиваются с проблемой «саргабля» (см. Википедию).3. как насчет функций, которые просто выбирают значение из таблицы? Если инкапсулирование UCASE() будет удалено, будет ли функция доступна для саргирования или недоступна для саргирования из-за того, что она является поиском?
4. Несогласованные параметры сортировки фактически являются вызовом функции. Именно об этом я и говорил в тексте своего Ответа.
5. Понятно; но предположим, что параметры сортировки в порядке; может ли ДЕТЕРМИНИРОВАННАЯ функция, которая извлекает значение из таблицы на основе переданных параметров, обрабатываться компилятором как допустимая? Я думаю, что в этом корень вопроса. Я бы подумал, что присутствие SELECT в функции может свести на нет любое настраиваемое состояние функции… моя цель состоит в том, чтобы просто обработать вызов функции как 1-кратную ссылку на константу; не более того.