2010-05-05 1 views
0

У меня есть 2 стола - пакеты и предметы. В таблице Items содержатся все элементы, относящиеся к пакетам, а также информация о местоположении. Как следующие образцы таблиц:Как вернуть набор результатов на основе других строк

Packages table 
id, type(enum{general,special}) 
1, general 
2, special 

Items table 
id, package_id, location 
1, 1, America 
2, 1, Europe 
3, 2, Europe 

Вопрос: Я хочу, чтобы найти все «специальные» пакеты, принадлежащих к месту и, если нет специального пакета не найден, то он должен вернуть «общие» пакеты, принадлежащие к тому же месту.

Так,

  1. для «Европы»: пакет 2 должен быть возвращен, так как это специальный пакет (Хотя пакет 1 также принадлежит Европе, но не требуется, так как его общий пакет)

  2. для «Америки»: пакет 1 должен быть возвращен, так как нет специальных пакетов

+0

Следует отметить, что «нет такого предположения, что специальные пакеты имеют более высокие идентификаторы. Я сделал меньшие таблицы только для ясности» -поверхность – Senseful

+0

@ Эгл: Я не понимаю, почему это вызывает путаницу, но разъясняет Кроме того, таблица пакетов имеет больше, чем «n» записей. Каждая запись имеет идентификатор и тип. Тип может быть «общим» или «специальным». – understack

+0

Извините, я неправильно понял вопрос. Кажется, я понимаю это сейчас. Я смущался с enum, думая, что он может содержать более двух разных значений. – Senseful

ответ

1

Вот два различных решения: (Примечание: я назвал поле перечислимого «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 каждый раз, когда вы используете один из своих старых запросов.

Смежные вопросы