Order a table by grouped columns

Posted in MySQL
Wednesday September 1st, 2010 07:53 PM

Para os exemplos abaixo criamos uma tabela pessoas e inserimos os registros abaixo. O código SQL para criar a tabela e inserir os registros seguem abaixo:

# criando a tabela pessoas
create table pessoas (
 id bigint(20) unsigned not null auto_increment,
 str_nome varchar(50) not null,
 int_altura int(5) not null,
 flo_peso float not null,
 int_felicidade int(1) not null,
 primary key (id)
);
# inserindo os registros
insert into pessoas values ( '1','Thiago','184','85.2','9');
insert into pessoas values ( '2','Matheus','175','90','8');
insert into pessoas values ( '3','Guilherme','188','78','6');
insert into pessoas values ( '4','Rafael','177','92','5');

Todos sabem que é possível ordenar uma consulta de banco de dados MySQL por multiplas colunas utilizando uma por vez:

select
 *
from
 pessoas
order by
 int_altura desc # primeiro ordenamos pela altura
 ,flo_peso desc # quando a altura for igual ordenamos pelo peso
;

O que acontece de fato, é que o primeiro valor é o utilizado para a ordenação, em caso de empate na ordeção a segunda coluna é utilizada e assim por diante. Mas e quando a necessidade é que as duas ou mais colunas tenham o mesmo peso e sejam levadas em consideração ao mesmo tempo? Para resolver esta demanda criei uma pequena função em MySQL que pode ajudar nesta tarefa.

# Criando a funcao
# autor: Tatto <mysql><tattodecastro.com>
delimiter $$ ;

drop function if exists fn_ordena_multiplos_campos $$

create function fn_ordena_multiplos_campos(
 fn_in_int_altura int( 5 )
 ,fn_in_flo_peso float
 ,fn_in_int_felicidade int( 1 )
) returns float
 deterministic
begin

 # Tratamento para a primeira interação, onde salvamos os valores iniciais para as comparações futuras.
 if isnull(@temporary_variable_altura_sdsdfe3) = true
 then
 set @temporary_variable_altura_sdsdfe3     = fn_in_int_altura;
 set @temporary_variable_peso_sdsdfe3       = fn_in_flo_peso;
 set @temporary_variable_felicidade_sdsdfe3 = fn_in_int_felicidade;
 return 1;
 else
 # Todos os retornos subsequentes são relativos ao primeiro.
 return (
 fn_in_int_altura/@temporary_variable_altura_sdsdfe3
 + fn_in_flo_peso/@temporary_variable_peso_sdsdfe3
 + fn_in_int_felicidade/@temporary_variable_felicidade_sdsdfe3
 )/3;
 end if;

end $$

delimiter ; $$

select
 *
from
 pessoas
order by
 # Ordenando pela função criada.
 fn_ordena_multiplos_campos(int_altura, flo_peso, int_felicidade) desc
;

Outras soluções como a criação de uma nova coluna que tenha os valores das colunas com as determinadas valências podem ser um solução. Só que para garantir a consistência dos dados a criação de um trigger ou outro componente de banco teria que ser feito. Uma terceira solução seria utilizar três vezes a consulta na mesma tabela criando uma coluna de valência. Só que a utilização da função demonstrou uma melhor performance.

Qualquer dúvida, entre em contato.
Um abraço, Tatto



Leave a Reply