Aqui estão duas soluções diferentes:(Nota:eu chamei o campo enum "package_type")
1ª solução (via função 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)
Esta solução essencialmente pega os locais e os une ao pacote com general (que se supõe existir; portanto,
inner join
) e pacote especial (que é opcional; portanto, left join
). Ele cria registros como este:location | general-package | [special-package]
Em seguida, ele usa o MySQL
IF
função para tentar primeiro escolher o ID do pacote especial e, em seguida, retornar ao ID do pacote geral. 2ª solução (via conversão de enum para inteiro):
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)
Esta solução explora o fato de que as enumerações são armazenadas como números inteiros. Ele converte o enum para um inteiro.
special
neste caso retornará 2
e general
retornará 1
. Como esses especiais são garantidos como superiores aos gerais neste caso (ou seja, 2> 1), podemos usar o MAX
função agregada. Agora temos essencialmente uma tabela dos locais e seu "pacote recomendado" (ou seja, especial se existir, geral caso contrário). Simplesmente juntamos isso à consulta normal junto com o tipo de pacote esperado e ele retorna os resultados corretos. Isenção de responsabilidade:não tenho certeza sobre a eficiência de nenhum desses métodos, portanto, você pode testar isso por conta própria.
Se você deseja reprojetar a tabela ou desnormalizá-la para obter eficiência, acho que esse design pode ser mais adequado:
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
A vantagem seria que é mais fácil aplicar várias regras no nível do banco de dados:
- Um local deve sempre ter um pacote geral (Items.general_package_id pode ser definido como NOT NULL)
- Um local deve ter apenas um único pacote geral (adicioná-lo em um campo em vez de uma junção garante que haja apenas um especificado)
- Um local pode ter no máximo um único pacote especial (adicioná-lo em um campo em vez de uma junção garante que haja apenas um especificado)
- Uma chave estrangeira em Items.general_package_id =GeneralPackages.id garantiria que essa coluna contém apenas pacotes válidos que são "gerais".
- O mesmo pode ser feito para special_package_id.
A desvantagem seria que você provavelmente precisaria usar um UNION ALL toda vez que usar uma de suas consultas antigas.