Я пытаюсь прочитать записи конфигурации из базы данных, которая имеет базовый «проект» и базовую «машину», определенные идентификатором 0. Базовая конфигурация (ключ + значение) может быть перезаписана более конкретными проектами и машинами. Я использую HeidiSQL в MariaDB.
редактировать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, я не знал, что результат может быть неоднозначным. Поэтому было принято решение, что проект всегда важнее машины.
Предоставьте полный и последовательный минимально воспроизводимый пример.
Я добавил, что использую MariaDB. @jarlh Чего вам не хватает в моем минимальном примере?
Соответствует ли указанный результат данным образца?
Я постарался изо всех сил, предоставив примеры данных и описав желаемый результат.
Предоставленные образцы данных и показанный желаемый результат должны полностью совпадать.
Не могли бы вы быть так любезны и показать мне, где они не совпадают? Я не понимаю проблему. Таблица в моем посте предназначена для демонстрации концепции. После этого я предоставил SQL для создания таблицы с примерами данных. После этого я предоставил SQL, который можно выполнить на основе этого образца данных. Он запускается и не возвращает никаких ошибок, но и никакого результата. Я описал, какую строку я хотел бы получить.
Я потратил несколько минут, пытаясь понять, о чем идет речь, и потерпел неудачу. Возможно, вам нужно поработать над формулировкой проблемы.
Value
в таблице (5000,4000,10000...) не совпадают значения в DDL (0,1,2...). Нет никаких оснований создавать такую двусмысленность в этом вопросе. Исправьте данные в DDL.
Если мы рассмотрим пару (project_id=1,machine_id=2) для project_id, значение по умолчанию для ключа Uc_max равно 4000, по умолчанию Machine_id равно 20000. Какое значение вы выберете и почему?
Спасибо @ValNik за более конкретный вклад. Я обновил таблицу, чтобы избежать путаницы. Вы обнаружили проблему в моей концепции базы данных. Большое спасибо. Мне нужно решить эту проблему, прежде чем думать о том, как получить строки.
На мой взгляд, наиболее подходящим способом является применение серии 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).
См. пример
С исходными данными
Этот запрос извлекает все значения для всех ключей для всех комбинаций (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
Этот запрос извлекает значение для назначенного (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
;
Если запрос возвращает более 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
Запросы с объединением полезны для управления данными.
Чтобы получить данные, используйте этот короткий запрос (возможно с объединением (...) для одного значения):
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
После превосходного вклада @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)
);
Извините за беспокойство и большое спасибо за помощь!
Какие СУБД вы используете? (HeidiSQL может подключаться к множеству разных серверов баз данных.)