MaxLevs
@MaxLevs

MySQL: как выбрать строки с учетом специфичности?

На стороне базы
Есть таблица, содержащая шаблоны сообщений бота Discord:
5cd200813bf8d679947397.png
Для каждого шаблона задаётся область применения (специфичность), с использованием колонок server_id (далее S), role_id (далее R), user_id (далее U) (не является уникальным ключом).
Могут быть заданы:
  • общесерверные шаблоны (S=null, R=null, U=null);
  • для конкретного пользователя (S=null, R=null, U="some_id_here");
  • для пользователя на каком-то сервере (S="some_id_here", R=null, U="some_id_here");
  • для роли на сервере (S="some_id_here", R="some_id_here", U=null);
  • для конкретного пользователя с установленной специальной ролью (S=null, R="some_id_here", U="some_id_here");
  • и так далее.

Понятно, что специфичность столбцов по-отдельности задаётся как S<R<U,
но вкупе, например, (S="some_id_here", R=null, U="some_id_here") > (S=null, R=null, U="some_id_here").

На стороне бота
Со стороны бота нам известны сервер - ServerID, адресат - UserID, список ролей адресата - UserRoles[].
Имея этот набор данных, требуется выбрать из базы все шаблоны, подпадающие под них и имеющие максимальную специфичность.

Ограничения
Бот написан на TypeScript, обращение к базе осуществляется через библиотеку Knex.js.
В связи с этим есть ряд ограничений. Бот должен уметь сам создавать таблицу в случае её отсутствия, поэтому таблица должна быть максимально проста. Триггеры отпадают.

Вопрос
Как организовать такую выборку?
Вообще, она осуществима в рамках заданных ограничений?

P.S. Бот написан не мной, связи с разработчиком нет. Я пишу модуль для него, и лезть в структуру бота не могу, поэтому ограничен его api.
  • Вопрос задан
  • 96 просмотров
Пригласить эксперта
Ответы на вопрос 3
VladimirAndreev
@VladimirAndreev
php web dev
А нагрузка большая сейчас и в планах?
Если нет, то я бы сделал несколько запросов:
1. С ограничениями по всем параметрам
2. Последовательно отбрасывая менее весомые критерии.

Если никакие фильтры не вернули ни одного ряда - то все критерии в null
Ответ написан
@AUser0
Чем больше знаю, тем лучше понимаю, как мало знаю.
А как такой вариант? Правда сам вес каждой записи, и случай наскольких записей с одинаковым весом нужно обрабатывать уже в скрипте, получающем данные...
SELECT * FROM table
WHERE user_id=X OR role_id IN (Y, Z, U) OR server_id=W
ORDER BY user_id=X DESC, role_id IN (Y, Z, U) DESC, server_id=W DESC
Ответ написан
Stalker_RED
@Stalker_RED
SELECT u, r, s, template,
CAST(
  CONCAT(
    CASE u WHEN null THEN 1 ELSE 0 END,
    CASE r WHEN null THEN 1 ELSE 0 END,
    CASE s WHEN null THEN 1 ELSE 0 END
  )
AS BINARY) priority
FROM tbl

WHERE (u=1 AND r=2 AND s=3)
  OR (u=1 AND r=2)
  OR (u=1 AND s=3)
  OR (r=2 AND s=3)
  OR u=1
  OR r=2
  OR s=3

having priority = max(priority) -- ну или order by

Как-то так, код не проверял, возможны опечатки.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы