MySQL — Наиболее эффективный способ обмена статическими значениями, используемыми в SQL, с внешними приложениями?

#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).

Итак, вопросы:

  1. Является ли это лучшим подходом к совместному использованию статических значений между двумя платформами (базой данных и серверной частью)?
  2. Почему движок 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-кратную ссылку на константу; не более того.