Вот два различных решения: (Примечание: я назвал поле перечислимого «package_t ип ")
функция первое решение (через IF()):
select
i.location,
if(ps.id is not null, ps.id, pg.id) as package_id
from
(select distinct location from Items) i
inner join
(select i.location, p.id
from Items i
inner join Packages p on (i.package_id = p.id and p.package_type = 'general')
) pg on (i.location = pg.location)
left join
(select i.location, p.id
from Items i
inner join Packages p on (i.package_id = p.id and p.package_type = 'special')
) ps on (i.location = ps.location)
Это решение по существу занимает места и к нему присоединяется к пакету с общей (которая, как предполагается существование; следовательно, inner join
) и специальный пакет (который является необязательным, следовательно, left join
). Это создает запись, такие как это:
location | general-package | [special-package]
Затем она использует функцию MySQL IF
для первой попытки выбрать ID специальный пакет, а затем возвращается к ID генеральному пакету.
второй раствор (с помощью литья перечисления на целое число):
select i.location, p.id
from
(select i.location, max(cast(package_type as unsigned)) as package_type
from Items i
left join Packages p on (i.package_id = p.id)
group by location
) i
inner join
(select i.location, p.id, p.package_type
from Items i
inner join Packages p on (i.package_id = p.id)
) p on (i.location = p.location and i.package_type = p.package_type)
Это решение использует тот факт, что перечисления хранятся в виде целых чисел. Он переводит перечисление в целое число. special
в этом случае вернется 2
и general
вернет 1
. Поскольку в этом случае эти специальные функции гарантированно выше общего (т. Е. 2> 1), мы можем использовать агрегатную функцию MAX
. Теперь у нас по существу есть таблица местоположений и их «рекомендуемый пакет» (т. Е. Специальный, если он существует, вообще в противном случае). Мы просто присоединяем это к обычным запросам вместе с ожидаемым типом пакета и возвращаем правильные результаты.
Отказ от ответственности: Я не уверен в эффективности любого из этих методов, поэтому вы можете протестировать это самостоятельно.
Если вы хотите либо перепроектировать таблицу или денормализовать его эффективности, я думаю, что эта конструкция может быть более подходящим:
GeneralPackages table
id, name
1, General Package 1
SpecialPackages table
id, name
1, Special Package 1
2, Special Package 2
Items table
id, general_package_id, special_package_id, location
1, 1, NULL, America
2, 1, 2, Europe
Преимущество будет то, что это легче применять несколько правил на уровне базы данных:
- в местах всегда должны иметь общий пакет (Items.general_package_id может быть определен как NOT NULL)
- Место должно иметь только один общий пакет (добавление его в поле, а не соединение, гарантирует, что имеется только один)
- Место может иметь не более одного специального пакета (добавление его в поле, а не соединение гарантирует, что есть только один указанный)
- Внешний ключ на Items.general_package_id = GeneralPackages.id гарантирует, что этот столбец содержит только действительные пакеты, которые являются «общими».
- То же самое можно сделать для special_package_id.
Недостатком является то, что вам, вероятно, потребуется использовать UNION ALL каждый раз, когда вы используете один из своих старых запросов.
Следует отметить, что «нет такого предположения, что специальные пакеты имеют более высокие идентификаторы. Я сделал меньшие таблицы только для ясности» -поверхность – Senseful
@ Эгл: Я не понимаю, почему это вызывает путаницу, но разъясняет Кроме того, таблица пакетов имеет больше, чем «n» записей. Каждая запись имеет идентификатор и тип. Тип может быть «общим» или «специальным». – understack
Извините, я неправильно понял вопрос. Кажется, я понимаю это сейчас. Я смущался с enum, думая, что он может содержать более двух разных значений. – Senseful