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

Я пытаюсь прочитать записи конфигурации из базы данных, которая имеет базовый «проект» и базовую «машину», определенные идентификатором 0. Базовая конфигурация (ключ + значение) может быть перезаписана более конкретными проектами и машинами. Я использую HeidiSQL в MariaDB.

идентификатор_проекта машинный_ид Ключ Ценить Интерпретация 0 0 Uc_max 5000 Машина 2 и Проект 2 1 0 Uc_max 4000 Машина 2 и Проект 1 0 1 Uc_max 10000 Машина 1 и Проект 2 1 1 Uc_max 15000 Машина 1 и Проект 1 0 2 Uc_max 20000 Машина 2 и Проект 1

редактировать1:

Столбец интерпретации показывает только один возможный способ интерпретации. Идея таблицы состоит в том, чтобы определить только те более конкретные значения, которые необходимы. Все комбинации, которые не указаны, по умолчанию должны соответствовать базовому проекту/машине. Так, например. запись для project_id 0 и Machine_id 0 также будет действительна для project_id 7 и Machine_id 9. Последняя запись (project_id 0 и Machine_id 2) действительна для машины 2 и всех проектов.


Я начал со следующей минимальной таблицы примера:

DROP TABLE IF EXISTS `overwrite`;
CREATE TABLE IF NOT EXISTS `overwrite` (
  `id` int(11) NOT NULL,
  `project_id` int(11) NOT NULL,
  `machine_id` int(11) NOT NULL,
  `key` char(50) NOT NULL DEFAULT '',
  `value` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

DELETE FROM `overwrite`;
INSERT INTO `overwrite` (`id`, `project_id`, `machine_id`, `key`, `value`) VALUES
    (0, 0, 0, 'Uc_max', 5000),
    (1, 1, 0, 'Uc_max', 4000),
    (2, 0, 1, 'Uc_max', 10000),
    (3, 1, 1, 'Uc_max', 15000),
    (4, 0, 2, 'Uc_max', 20000);

С моим текущим запросом я могу правильно прочитать первые четыре записи. К сожалению, когда я пытаюсь прочитать конфигурацию проекта 1 и машины 2, запись не отображается. Поскольку записи для проекта 1 не существует, по умолчанию он должен иметь значение 0 и возвращать запись с идентификатором 4.

SELECT o.`id`, o.`project_id`, o.`machine_id`, o.`key`, o.`value`
FROM overwrite o
LEFT JOIN (
     -- get all keys, for specific project_id
    SELECT DISTINCT o1.`key`
    FROM overwrite o1
    -- insert specific project_id here
     WHERE o1.`project_id` = 1
) subquery_project ON o.`key` = subquery_project.`key`
LEFT JOIN (
    -- Get all keys for specific machine_id
    SELECT DISTINCT o1.`key`
    FROM overwrite o1
    -- insert specific machine_id here
    WHERE o1.`machine_id` = 2
) subquery_machine ON o.`key` = subquery_machine.`key`
-- always read entries for 0, replace second ID with specifc project / machine
WHERE o.`project_id` IN (0, 1)
AND o.`machine_id` IN (0, 2)
AND (
    -- If project_id is 0, include only if there is no more specific projects entry
    (o.`project_id` = 0 AND subquery_project.`key` IS NULL)
    -- Always include more specific project entries
    OR o.`project_id` = 1
)
AND (
    -- If machine_id is 0, include only if there is no more specific machine entry
    (o.`machine_id` = 0 AND subquery_machine.`key` IS NULL)
    -- Always include more specific machine entries
    OR o.`machine_id` = 2
)

Я понимаю, что subquery_project.key в этом случае НЕ будет нулевым и вызывает проблему. Тем не менее я не могу понять, как изменить запрос или создать другой, который работает нормально.


редактировать2: Я хочу сохранить и получить конфигурации для конкретного проекта, но также хочу учитывать глобальные конфигурации (project_id = 0) только в том случае, если для конкретного проекта нет переопределения. Таким образом, в базе данных сохраняются только переопределения и глобальная конфигурация. Ключи, для которых не существует специального переопределения, будут иметь значения глобальной конфигурации (project_id = 0). Это стало более сложным, поскольку глобальная конфигурация существует не только для каждого проекта, но и для каждой машины.

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

Моя проблема в том, что я не могу во всех случаях получить правильные значения конфигурации. SELECT выше должен возвращать строку с идентификатором 4, но ничего не возвращает. Если я настрою его, чтобы найти действительные конфигурации для проекта 7 и машины 1, он правильно вернет строку с идентификатором 2:

SELECT o.`id`, o.`project_id`, o.`machine_id`, o.`key`, o.`value`
FROM overwrite o
LEFT JOIN (
     -- get all keys, for specific project_id
    SELECT DISTINCT o1.`key`
    FROM overwrite o1
    -- insert specific project_id here
     WHERE o1.`project_id` = 7
) subquery_project ON o.`key` = subquery_project.`key`
LEFT JOIN (
    -- Get all keys for specific machine_id
    SELECT DISTINCT o1.`key`
    FROM overwrite o1
    -- insert specific machine_id here
    WHERE o1.`machine_id` = 1
) subquery_machine ON o.`key` = subquery_machine.`key`
-- always read entries for 0, replace second ID with specifc project / machine
WHERE o.`project_id` IN (0, 7)
AND o.`machine_id` IN (0, 1)
AND (
    -- If project_id is 0, include only if there is no more specific projects entry
    (o.`project_id` = 0 AND subquery_project.`key` IS NULL)
    -- Always include more specific project entries
    OR o.`project_id` = 7
)
AND (
    -- If machine_id is 0, include only if there is no more specific machine entry
    (o.`machine_id` = 0 AND subquery_machine.`key` IS NULL)
    -- Always include more specific machine entries
    OR o.`machine_id` = 1
)

редактировать3: обновил значения в таблице SQL так, чтобы они были такими же, как в таблице, чтобы избежать путаницы.


редактировать 4: Как выяснил @ValNik, я не знал, что результат может быть неоднозначным. Поэтому было принято решение, что проект всегда важнее машины.

Какие СУБД вы используете? (HeidiSQL может подключаться к множеству разных серверов баз данных.)

jarlh 26.08.2024 11:27

Предоставьте полный и последовательный минимально воспроизводимый пример.

jarlh 26.08.2024 11:27

Я добавил, что использую MariaDB. @jarlh Чего вам не хватает в моем минимальном примере?

seuk 26.08.2024 11:51

Соответствует ли указанный результат данным образца?

jarlh 26.08.2024 11:54

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

seuk 26.08.2024 13:00

Предоставленные образцы данных и показанный желаемый результат должны полностью совпадать.

Akina 26.08.2024 13:53

Не могли бы вы быть так любезны и показать мне, где они не совпадают? Я не понимаю проблему. Таблица в моем посте предназначена для демонстрации концепции. После этого я предоставил SQL для создания таблицы с примерами данных. После этого я предоставил SQL, который можно выполнить на основе этого образца данных. Он запускается и не возвращает никаких ошибок, но и никакого результата. Я описал, какую строку я хотел бы получить.

seuk 26.08.2024 14:34

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

Ian 26.08.2024 14:53
Value в таблице (5000,4000,10000...) не совпадают значения в DDL (0,1,2...). Нет никаких оснований создавать такую ​​двусмысленность в этом вопросе. Исправьте данные в DDL.
ValNik 26.08.2024 16:37

Если мы рассмотрим пару (project_id=1,machine_id=2) для project_id, значение по умолчанию для ключа Uc_max равно 4000, по умолчанию Machine_id равно 20000. Какое значение вы выберете и почему?

ValNik 26.08.2024 16:44

Спасибо @ValNik за более конкретный вклад. Я обновил таблицу, чтобы избежать путаницы. Вы обнаружили проблему в моей концепции базы данных. Большое спасибо. Мне нужно решить эту проблему, прежде чем думать о том, как получить строки.

seuk 26.08.2024 16:56
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
0
11
70
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

На мой взгляд, наиболее подходящим способом является применение серии JOIN для выбора specifc values для кортежа (ключ, проект, машина), а также значений по умолчанию.

Там основная таблица - список кортежей(ключ,project_id,machine_id).
Таблица первого соединения overwrite - при полностью подходящем ключе,project_id,machine_id.
Второе соединение — по ключу, проект с Machine_id=0 — это настройки проекта по умолчанию.
Третье соединение — по ключу, машина с project_id=0 — это настройки машины по умолчанию.
Последнее ПРИСОЕДИНЕНИЕ — по ключу с project_id=0 и machine_id=0 — значения ключа по умолчанию.

Значение результата сначала не является нулевым значением из (project_machine,project default,machine default,key default).

См. пример

С исходными данными

идентификатор идентификатор_проекта машинный_ид ключ ценить 0 0 0 Uc_max 5000 1 1 0 Uc_max 4000 2 0 1 Uc_max 10000 3 1 1 Uc_max 15000 4 0 2 Uc_max 20000

Этот запрос извлекает все значения для всех ключей для всех комбинаций (key,project_id,machine_id) в таблице overwrite.

with mp as(
   select p.project_id,m.machine_id,k
   from (select distinct project_id from overwrite where project_id<>0
      ) p
   cross join (select distinct machine_id from overwrite where machine_id<>0
      ) m 
   cross join (select distinct `key` as k from overwrite 
      ) k
)
select mp.project_id,mp.machine_id,mp.k
  ,coalesce(pm.value,pd.value,md.value,def.value) key_value  -- result key
  ,pm.value project_machine_value -- project and machine
  ,pd.project_id pd_project_id,pd.value project_default -- project defaults
  ,md.machine_id md_machine_id,md.value machine_default -- machine defaults
  ,def.value key_default_value -- default value for key
from mp
left join overwrite pm on pm.key=mp.k and pm.project_id=mp.project_id 
  and pm.machine_id=mp.machine_id
left join overwrite pd on pd.key=mp.k and pd.project_id=mp.project_id 
  and pd.machine_id=0
left join overwrite md on md.key=mp.k and md.machine_id=mp.machine_id 
  and md.project_id=0
left join overwrite def on def.key=mp.k 
  and def.machine_id=0 and def.project_id=0
order by mp.project_id,mp.machine_id
идентификатор_проекта машинный_ид к ключ_значение project_machine_value pd_project_id project_default md_machine_id машина_по умолчанию key_default_value 1 1 Uc_max 15000 15000 1 4000 1 10000 5000 1 2 Uc_max 4000 нулевой 1 4000 2 20000 5000

Этот запрос извлекает значение для назначенного (key,project_id,machine_id).

with mp as(
   select 1 as project_id,7 as machine_id,'Uc_max' as k
)
select mp.project_id,mp.machine_id,mp.k
  ,coalesce(pm.value,pd.value,md.value,def.value) key_value  -- result key
  ,pm.value project_machine_value -- project and machine
  ,pd.project_id pd_project_id,pd.value project_default -- project defaults
  ,md.machine_id md_machine_id,md.value machine_default -- machine defaults
  ,def.value key_default_value -- default value for key
from mp
left join overwrite pm on pm.key=mp.k and pm.project_id=mp.project_id 
  and pm.machine_id=mp.machine_id
left join overwrite pd on pd.key=mp.k and pd.project_id=mp.project_id 
  and pd.machine_id=0
left join overwrite md on md.key=mp.k and md.machine_id=mp.machine_id 
  and md.project_id=0
left join overwrite def on def.key=mp.k 
  and def.machine_id=0 and def.project_id=0
;
идентификатор_проекта машинный_ид к ключ_значение project_machine_value pd_project_id project_default md_machine_id машина_по умолчанию key_default_value 1 7 Uc_max 4000 нулевой 1 4000 нулевой нулевой 5000

Если запрос возвращает более 1 строки, то в таблице имеется неоднозначность — вам необходимо решить, что делать в этом случае.

рабочий пример

Пример различных комбинаций значений project_id,machine_id

with mp as(
   select 1 as project_id,7 as machine_id,'Uc_max' as k
  union all
  select 2 as project_id,1 as machine_id,'Uc_max' as k
  union all
  select 2 as project_id,3 as machine_id,'Uc_max' as k
)

select mp.project_id,mp.machine_id,mp.k
  ,coalesce(pm.value,pd.value,md.value,def.value) key_value  -- result key
  ,pm.value project_machine_value -- project and machine
  ,pd.project_id pd_project_id,pd.value project_default -- project defaults
  ,md.machine_id md_machine_id,md.value machine_default -- machine defaults
  ,def.value key_default_value -- default value for key
from mp
left join overwrite pm on pm.key=mp.k and pm.project_id=mp.project_id 
  and pm.machine_id=mp.machine_id
left join overwrite pd on pd.key=mp.k and pd.project_id=mp.project_id 
  and pd.machine_id=0
left join overwrite md on md.key=mp.k and md.machine_id=mp.machine_id 
  and md.project_id=0
left join overwrite def on def.key=mp.k 
  and def.machine_id=0 and def.project_id=0
идентификатор_проекта машинный_ид к ключ_значение project_machine_value pd_project_id project_default md_machine_id машина_по умолчанию key_default_value 2 1 Uc_max 10000 нулевой нулевой нулевой 1 10000 5000 1 7 Uc_max 4000 нулевой 1 4000 нулевой нулевой 5000 2 3 Uc_max 5000 нулевой нулевой нулевой нулевой нулевой 5000

рабочий пример

Запросы с объединением полезны для управления данными.

Чтобы получить данные, используйте этот короткий запрос (возможно с объединением (...) для одного значения):

select *
  ,(select max(value) value from overwrite t1 
    where t1.`key`=t.`key` and t1.project_id=t.project_id and t1.machine_id=t.machine_id)pm
  ,(select max(value) value from overwrite t2 
    where t2.`key`=t.`key` and t2.project_id=t.project_id and  t2.machine_id=0)pv
  ,(select max(value) value from overwrite t3
    where t3.`key`=t.`key` and t3.project_id=0 and t3.machine_id=t.machine_id )mv
  ,(select max(value) value from overwrite t4
    where t4.`key`=t.`key` and t4.project_id=0 and t4.machine_id=0)dv
from ( select 'Uc_max' as `key`, 1 project_id,2 machine_id) t

ключ идентификатор_проекта машинный_ид вечер пв МВ дв Uc_max 1 2 нулевой 4000 20000 5000

рабочий пример

Ответ принят как подходящий

После превосходного вклада @ValNik и решения отдать приоритет проекту над машиной, это кажется намного проще, чем я думал, и следующий запрос, похоже, правильно считывает все ключи конфигурации.

SELECT o.id, o.project_id, o.machine_id, o.key, o.value
FROM overwrite o
WHERE o.id = COALESCE(
    (SELECT o1.id FROM overwrite o1 WHERE o1.key = o.key AND o1.project_id = 1 AND o1.machine_id = 2 LIMIT 1),
    (SELECT o2.id FROM overwrite o2 WHERE o2.key = o.key AND o2.project_id = 1 AND o2.machine_id = 0 LIMIT 1),
    (SELECT o3.id FROM overwrite o3 WHERE o3.key = o.key AND o3.project_id = 0 AND o3.machine_id = 2 LIMIT 1),
    (SELECT o4.id FROM overwrite o4 WHERE o4.key = o.key AND o4.project_id = 0 AND o4.machine_id = 0 LIMIT 1)
);

Извините за беспокойство и большое спасибо за помощь!

Другие вопросы по теме