Ordenar resultados numa determinada sequência em MySQL

Existe projectos que são uma autêntica dor de cabeça, no entanto, aprendemos muito com eles.

Imaginem que temos que ordenar um determinado resultado de forma ascendente, mas queremos que um determinado valor tenha prioridade sobre os outros valores e que saia em primeiro lugar.

Para evitar linhas de programação desnecessárias vamos analisar uma solução para MySQL.

Analisemos a seguinte tabela “demos”:
Tabela "demos"

Ordenamos a tabela ascendentemente pelo valor:

SELECT * FROM demos ORDER BY value ASC;

Resultado:
Ordenar Tabela Por Valor

Até aqui tudo muito simples e o resultado foi o esperado. No entanto, quero que o valor “Value4″ da coluna “value” saia primeiro que todos e que os restantes valores continuem ordenados ascendentemente. Basicamente quero definir este valor como um valor prioritário, um valor relevante.

É possível? Se pode fazer?

Sim, é possível Para isso, vamos ter de usar “ORDER BY FIELD” no comando SQL.

Vamos ordenar a tabela ascendentemente pelo valor e dar relevância ao valor “Value4″:

SELECT * FROM demos ORDER BY FIELD(value, 'Value4') DESC, value ASC;

Resultado:
Ordenar Tabela Por Valor e ORDER BY FIELD

Como podem ver, o primeiro resultado é o valor “Value4″ e todos os demais estão ordenados ascendentemente. O que acabamos de fazer foi dizer à base de dados, através da instrução “ORDER BY FIELD(value, ‘Value4′ DESC)“, que queríamos que o valor “Value4″ fosse o primeiro a sair (que fosse o valor mais relevante).

Imaginem que agora queremos 2 valores prioritários (Value4 e Value8) e que os restantes valores estejam ordenados descendentemente.

Como fazemos?

Analisemos o seguinte comando SQL:

SELECT * FROM demos ORDER BY FIELD(value, 'Value8', 'Value4') DESC, value DESC;

Resultado:
Ordenar Tabela Por Valor e ORDER BY FIELD

O que fizemos foi adicionar mais um valor prioritário, neste caso o valor “Value8″, e mudamos “value ASC” a “value DESC” para que os restante valores ficassem ordenados descendentemente.

Vamos complicar um pouco mais o tema…

Analisemos a seguinte tabela “demos”:
Tabela "demos"

Ordenamos a tabela ascendentemente pelo valor:

SELECT * FROM demos ORDER BY value ASC;

Resultado:
Ordenar Tabela Por Valor

Obviamente este não é o melhor formato para guardar um valor de tempo (H:MM:SS em formato texto), mas imaginem que é uma base de dados de um projecto mais antigo e queremos trabalhar com os seus valores.

Obviamente o resultado não foi o esperado, porque no que se refere a valores de tempo, tanto as 8h como as 9h deveriam sair primeiro que as 10h.

Como solucionamos este problema?

Vamos a solucionar este problema através da instrução SQL referida acima (”ORDER BY FIELD“).

Analisemos o seguinte comando SQL:

SELECT * FROM demos ORDER BY FIELD(LEFT(value,2),'9:','8:') DESC, value ASC;

Resultado:
Ordenar Tabela Por Valor e ORDER BY FIELD

Agora o resultado foi o esperado. Já temos as 8h e as 9h primeiro que as 10h.

O que fizemos neste caso, foi dizer à base de dados que queríamos ordenar apenas os primeiros 2 caracteres de cada valor na ordem referida, isto é, primeiro os valores que começam por “8:” e depois os valores que começam por “9:”.

Na instrução SQL os valores saem ao contrário. Isto é assim porque estamos ordenando descendentemente os valores da instruçao “FIELD“. Estes valores são ordenados descendentemente para que possam sair nas primeiras posições do resultado.

São situações difíceis de encontrar, no entanto, fica a minha solução

Espero que esta informação vos seja útil…

13 Comentários a “Ordenar resultados numa determinada sequência em MySQL”

  1. [...] [PT] Ordenar resultados en una determinada secuencia en MySQL http://www.setpointer.com/blog/mysql/ordenar-resultados-numa-determ…  por Pardalito hace 3 segundos [...]

  2. Andrius diz:

    Opa…..

    cara olha só…

    tenho uma tabela com os seguintes campos: id, nome, pacela1, valor1, recebido1, pacela2, valor2, recebido2, pacela3, valor3, recebido3

    os campos no mysql são do tipo date
    preciso ordenar por data de parcela assim:

    parcela1 = 01-01-2010
    parcela1 = 02-01-2010
    parcela1 = 10-01-2010
    parcela2 = 05-02-2010

    mas estou conseguindo apenas o seguinte resultado

    parcela1 = 01-01-2010
    parcela2 = 05-02-2010
    parcela1 = 02-01-2010
    parcela1 = 10-01-2010

    teria como me dar uma força??
    vlw

  3. rpereira diz:

    Primeiro penso que a questão não esta bem formulada porque os resultados não podem ser esses.

    Penso que os resultados para a tabela em questão deveriam ser algo como isto:

    parcela1 | parcela2
    01-01-2010 | 02-01-2010
    10-01-2010 | 05-02-2010

    Também não sei a consulta (pode ser uma de exemplo) que estas a usar.

    No entanto, posso-te dizer que para este tipo de situações nunca utilizes datas em formato “date” porque depois tens este tipo de problemas.

    Usa sempre que possas datas no formato “timestamp“. Veras que te resolve muitos problemas e depois com PHP ou outra linguagem podes formatear a data como queiras.

    Se me dás mais informação, tenho todo o gosto de tentar ajudar-te neste problema em concreto e com os valores da data no formato referido.

    Que falta:
    * Uma consulta de exemplo para ver o que podes estar a fazer mal.
    * Valores de saída concretos, porque o formato de resultado que utilizas para mim não é valido e não consigo entender o problema.

    Formato de resultado valido em MySQL:
    id | name | value
    1 | Nome1 | Valor1
    2 | Nome2 | Valor2
    3 | Nome3 | Valor3
    4 | Nome4 | Valor4

  4. Andrius diz:

    cara olha só…

    tenho uma tabela com os seguintes campos: id, cliente, pacela1, valor1, recebido1, pacela2, valor2, recebido2, pacela3, valor3, recebido3

    e preciso listar da seguinte forma: por ordem de data – começando pela parcela1
    id – 01
    cliente – pedro
    parcela1 – 01-01-2010
    valor – 0.00

    id – 02
    cliente – joão
    parcela1 – 02-01-2010
    valor – 0.00

    id – 01
    cliente – pedro
    parcela2 – 03-01-2010
    valor – 0.00

    mas estou conseguindo apenas o seguinte resultado

    id – 01
    cliente – pedro
    parcela1 – 01-01-2010
    valor – 0.00

    id – 01
    cliente – pedro
    parcela1 – 03-01-2010
    valor – 0.00

    id – 02
    cliente – joão
    parcela2 – 02-01-2010
    valor – 0.00

    meu sql

    SELECT tb1.id, tb1.titulo, tb1.cliente, tb2.* FROM sd_jobs tb1, sd_receita tb2 where (tb2.parcela_01 BETWEEN ‘”.$data_ini.”‘ AND ‘”.$data_fim.”‘) and tb2.recebido_01 = ‘0000-00-00′ and tb1.id = tb2.id_job or (tb2.parcela_02 BETWEEN ‘”.$data_ini.”‘ AND ‘”.$data_fim.”‘) and tb2.recebido_02 = ‘0000-00-00′ and tb1.id = tb2.id_job ORDER BY tb2.parcela_01 ASC

    teria como me dar uma força??
    pois não está ficando em ordem pela parcela1 e sim pelo id….

    há uma possibilidade de conseguir o resultado que preciso??

    vlw

  5. rpereira diz:

    Tento entender o que me dizes e penso que percebi, no entanto, a pergunta tens muitas incoerências de novo.

    Quando parcela1 tem valor, qual é o valor da parcela2 já que esta na mesma tabela.
    Quando parcela2 tem valor, qual é o valor da parcela1 já que esta na mesma tabela.
    Quando te referes a valor a que campo nos referimos valor1 ou valor2?

    Assumindo que o resultado seja este:

    id | cliente | parcela1 |valor1 | parcela2 | valor2
    01 | pedro | 01-01-2010 | 0.00 | ???? | ????
    02 | joao | 02-01-2010 | 0.00 | ???? | ????
    01 | pedro | ???? | ???? | 03-01-2010 | 0.00

    Caso esses ???? não sejam valores vazios, ordenar a saida por duas colunas é relativamente facil, sempre que se ordene primeiro uma e depois a outra:

    Exemplo:

    SELECT * FROM table WHERE XX=XX ORDER BY parcela1 ASC, parcela2 ASC;

    id | cliente | parcela1 |valor1 | parcela2 | valor2
    01 | pedro | 01-01-2010 | 0.00 | 01-01-2010 | ????
    02 | joao | 02-01-2010 | 0.00 | 01-01-2010 | ????
    01 | pedro | 02-01-2010 | ???? | 03-01-2010 | 0.00
    03 | juca | 02-01-2010 | ???? | 05-01-2010 | 0.00

    Caso esses ???? sejam valores vazios, entao é mais complicado, porque queres que se ordene os resultados por duas columas mas basicamente como se a coluna parcela1 e parcela2 estivessem fusionadas e o resultado desta fusão fosse ascendentemente.

    É possivel? Penso que sim (depende se assumi bem o que me tentas pedir), mas envolve variaveis em MySQL ou um pouco de programaçao MySQL como Views… Bastante complicado.

    Exemplo:

    SELECT *, IF(ISNULL(parcela1), parcela2 ,parcela1) AS parcelas FROM dates ORDER BY parcelas ASC;

    id | cliente | parcela1 |valor1 | parcela2 | valor2
    01 | pedro | 01-01-2010 | 0.00 | null | null
    02 | joao | 02-01-2010 | 0.00 | null | null
    01 | pedro | null | nul | 03-01-2010 | 0.00

    O que fizemos aqui foi usar um pouco de programaçao logica, criar um campo virtual “parcelas” e ordenar o resultado deste campo ascendentemente.
    Que temos? O resultado que desejas.

    Insisto que é para os casos em que parcela1 e valor1 sejam nulos.

    Caso estejam vazios em ves de nulos, entao podes usar o seguinte exemplo:

    SELECT *, IF(parcela1, parcela2 ,parcela1) AS parcelas FROM dates ORDER BY parcelas ASC;

    id | cliente | parcela1 |valor1 | parcela2 | valor2
    01 | pedro | 01-01-2010 | 0.00 | |
    02 | joao | 02-01-2010 | 0.00 | |
    01 | pedro | | | 03-01-2010 | 0.00

    Uma vez mais o resultado é o resultado que necesitas.

    Tudo depende do que queiras e da logica que lhe queiras dar.

    Outra coisa, quanto melhor expliques mais rapido chegamos a uma soluçao. Não quero saber detalhes dos teus projectos. Podes usar até consultas de exemplo como eu faço, mas preciso sempre do maximo de informaçao que me possas dar e a representaçao dos resultados é de extrema importancia.

    Espero que um destes exemplos seja a soluçao para o teu problema. Em caso afirmativo, dá-me um feedback :oP

  6. Andrius diz:

    Bom dia

    amigo, para facilitar as coisas segue o link de uma imagem do meu banco
    e o sql da consulta…..

    com aplicação do exemplo que vc me passou….
    mas ainda sem sucesso!

    SELECT *, IF(parcela_01, parcela_02, parcela_01) as parcelas FROM sd_receita where (parcela_01 BETWEEN ‘2010-01-01′ AND ‘2010-02-28′) and recebido_01 = ‘0000-00-00′ ORDER BY parcelas ASC

    caso precise mais alguma informação
    andrius******@******.com

    ahh
    obrigado pela ajuda

    vou tentando por aki….

    vlw

  7. rpereira diz:

    Como o campo “parcela1” tem uma data inferior à data do campo “parcela2”, entao caso “parcela2” seja maior que “parcela1”, o campo virtual “parcelas” vai ficar com o valor do campo “parcela2”.
    Caso seja menor, o campo virtual “parcelas” vai ficar com o valor do campo “parcela1”.

    Como se traduz isto em SQL? Assim:

    SELECT *, IF(parcela2 > parcela1, parcela2 ,parcela1) AS parcelas FROM dates ORDER BY parcelas ASC;

    Aqui temos a informação sem estar ordenada:

    Aqui temos a informação devidamente ordenada usando o SQL acima referido:

    Espero que desta vez tenha acertado em cheio…

  8. k00dez diz:

    Procurei por tudo e aqui foi o lugar onde tinha maior quantidade de informações sobre ordenação de resultados.
    Se possível gostaria de expor a minha dúvida. Tenho um campo em uma tabela que armazena valores de maneira simples, por exemplo:

    1.200,00
    700,00
    12.500,00

    A minha pergunta é: Existe uma maneira de ordernar por valor do menor para o maior?

    Hoje a minha consulta é como o exemplo abaixo:

    $sql = mysql_query(”SELECT * FROM `tabela` WHERE campo1 =’$campo1′ ORDER BY valor DESC”) or die(mysql_error());
    while($agrupa = mysql_fetch_array($sql))
    {

    Desde já agradeço por qualquer ajuda que conseguir.

  9. rpereira diz:

    Olá,

    Podia haver mais informação, pena que por motivos de tempo afinal tenho este pequeno cantinho esquecido.

    Sobre o teu tema… Não se deve de adicionar numa base de dados valores com esse formato. (!!!???)

    Devias adicionar valores com o seguinte formato:
    1200.00
    700.00
    12500.00
    745.55

    Depois quando pedes esses dados à base de dados só precisas de mudar para o formato que necessitas através de uma função de PHP. Para o teu caso:
    number_format($number, 2, ‘,’, ‘.’)

    Algo como isto:
    < ?php echo number_format('12500.00', 2, ',', '.'); ?>

    Se realmente precisas de valores com esse formato na base de dados (!!!???), vai ser difícil conseguir uma soluçao.

    Se é assim, volta a comentar e diz-me também que tipo de dados usas para este campo em concreto (INT, VARCHAR, …).

    Espero ter ajudado…

  10. rpereira diz:

    Se realmente tem de ficar no formato que me pedes (que nao aconselho), deberias de usar a seguinte Query:
    SELECT * FROM price ORDER BY CAST(REPLACE(REPLACE(price, ‘.’, ”), ‘,’, ‘.’) AS DECIMAL(12,2)) ASC;

    Donde price é o campo com os valores que me indicaste.
    12 – O numero de digitos inteiros que necessitas.
    2 – O numero de digitos decimais que necessitas.

    Volto a recordar, que este nao é o modo correcto de fazer as coisas…

    Aguardo um feedback para saber como ficou o tema.

  11. k00dez diz:

    Deu certo…. muito obrigado!!!
    Funcionou dessa maneira:

    number_format($monta['valorus'],2,’,',’.');

    E consegui a ordenação por valor utilizando dessa maneira:

    $sql = mysql_query(”SELECT * FROM `tabela` WHERE campo1 =’$campo1′ ORDER BY CAST(valor AS SIGNED) ASC”)

    Muito obrigado!!!!

  12. rpereira diz:

    Fico contente…

    Se utilizas a Query com CAST nao precisas do number_pormat para nada…

    O number_format só é necessario se insertas os valores correctamente na base de dados (12500.00 – FLOAT).

    Para inserir codigo basta copiar e pegar deste o notepad sem as tags tipicas de php (< ?php).

    Até uma proxima…

Comentar