dblearn94
@dblearn94

Избыточные данные или сложная логика запроса?

Привет, есть 2 таблицы, упрощу для примера:

1. setting:
id | name
2. setting_value:
id | setting_id | domain_id | language_id | value
domain_id (если null - то настройка для общая для всех сайтов)
language_id (если null - то настройка для всех языков)

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

И тут вот проблема и большой вопрос, как лучше сделать:
1 (как сделано сейчас):
получаю сложным на мой взгляд вопросом, с такой логикой:
Получаем данные для текущего домена (32) и языка (15).
UNION
Получаем данные для текущего домена (32) стандартного языка (1), исключая setting_id полученные из прошлого запроса.
UNION
Получаем данные для текущего домена (32) и общего языка (null), исключая прошлые запросы
UNION
Получаем данные для стандартного домена (1) и текущего языка (15), исключая прошлые запросы

И т.д пока не переберем все варианты


2. Просто наплодить копии, чтобы для каждого языка, каждой настройки - существовала запись и получать ее по текущим параметрам запросом:
SELECT * FROM `setting_value` WHERE `domain_id` = 32 AND `language_id` = 15

Но данные будут расти как по экспоненте.
например:
20 настроек для 100 поддоменов + 10 языков = 2000 записей в setting_value, большинство из которых будут копиями

Но тут такое дело, на чаше весов:
  1. Сложный запрос состоящий из 9 обьедененных запросов
  2. Избыточные данные


Как это организовать адекватно ?
  • Вопрос задан
  • 142 просмотра
Решения вопроса 1
erge
@erge
Примус починяю
Одни и те же вопросы с разных аккаунтов??

я так понимаю что это тоже самое или суть аналогична: Получить строку с максимальным совпадением? ??
вместо `movie_desc` - `setting_value` (смотри ниже запрос и примеры)

через оконные функции надо делать, если на MySQL 8 (СУБД вы НЕ указали...), функция ранжирования - row_number()

-- здесь я вынес параметры (домены, языки) в отдельное представление param,
-- которое определяется перед SELECT
-- это сделано, для того чтобы по всему запросу не искать и вписывать их
-- меняется все в одном месте:
WITH param AS (
  SELECT
      2 AS primary_domain_id,    -- id первичного домена
      1 AS secondary_domain_id,  -- id вторичного домена
      2 AS primary_language_id,  -- id первичного языка
      1 AS secondary_language_id -- id вторичного языка
    FROM dual
)
SELECT *
  FROM (
    SELECT m.*,
        row_number() over (
          partition by m.movie_id
          order by
          	case
          	  when m.domain_id = p.primary_domain_id then 100
          	  when m.domain_id = p.secondary_domain_id then 50
          	  else 1
            end DESC,
          	case
          	  when m.language_id = p.primary_language_id then 100
          	  when m.language_id = p.secondary_language_id then 50
          	  else 1
            end DESC
        ) rn
      FROM `movie_desc` m, param p
      WHERE (m.domain_id = p.primary_domain_id
          OR m.domain_id = p.secondary_domain_id
          OR m.domain_id IS NULL)
	    AND (m.language_id = p.primary_language_id
          OR m.language_id = p.secondary_language_id
          OR m.language_id IS NULL)
  ) t
  WHERE t.rn = 1
;


Примеры на:
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=f46bf1...
https://www.db-fiddle.com/f/8UigbxU6uNg6guU8ygKgrA/1

PS: если MySQL более древний чем 8й, то попробуйте переписать как описано на второй странице той же статьи , ссылка на которую приведена выше.
www.sql-tutorial.ru/ru/book_row_number_function/pa...
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

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

Войти через центр авторизации
Похожие вопросы