Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Como retornar o conjunto de resultados com base em outras linhas


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.