Pergunta

Nossa aplicação tem um campo CustomerNumber. Nós temos centenas de pessoas diferentes, utilizando o sistema (cada um tem seu próprio login e sua própria lista de CustomerNumbers). Um usuário individual pode ter, no máximo, 100.000 clientes. Muitos têm menos de 100.

Algumas pessoas só colocar números reais em seus campos de número de clientes, enquanto outros usam uma mistura de coisas. O sistema permite a 20 caracteres que podem ser A-Z, 0-9 ou um traço, e armazena estes em um VARCHAR2 (20). Qualquer coisa minúscula é feita em maiúsculas antes de serem armazenados.

Agora, vamos dizer que temos um simples relatório que lista todos os clientes para um usuário em particular, classificado por número do cliente. por exemplo.

SELECT CustomerNumber,CustomerName
FROM Customer
WHERE User = ?
ORDER BY CustomerNumber;

Esta é uma solução ingênua quanto as pessoas que sempre apenas utilizam números não querem ver uma espécie alfabética simples (onde "10" vem antes de "9").

Eu não desejo para perguntar ao usuário quaisquer perguntas desnecessárias sobre os seus dados.

Eu estou usando Oracle, mas eu acho que seria interessante ver algumas soluções para outros bancos de dados. Por favor, inclua qual banco de dados a sua resposta trabalha.

O que você acha que a melhor maneira de implementar isso?

Foi útil?

Solução

No Oracle 10g:

SELECT  cust_name
FROM    t_customer c 
ORDER BY
    REGEXP_REPLACE(cust_name, '[0-9]', ''), TO_NUMBER(REGEXP_SUBSTR(cust_name, '[0-9]+'))

Isto irá classificar pela primeira ocorrência de número, não em relação a sua posição, i. e:.

  1. customer1 < customer2 < customer10
    • cust1omer ? customer1
    • cust8omer1 ? cust8omer2

, onde um meio ? que a ordem é indefinida.

Que é suficiente para a maioria dos casos.

Para forçar a ordem de classificação em caso 2, você pode adicionar um REGEXP_INSTR(cust_name, '[0-9]', n) aos tempos lista ORDER BY n, ordem forçando na primeira aparição de n-th (2nd, 3rd etc.) grupo de dígitos.

Para forçar a ordem de classificação em caso 3, você pode adicionar um TO_NUMBER(REGEXP_SUBSTR(cust_name, '[0-9]+', n)) aos tempos lista ORDER BY n, a fim de n-th forçando. grupo de dígitos.

Na prática, a consulta que eu escrevi é suficiente.

Você pode criar um índice baseado em função de essas expressões, mas você vai precisar para forçá-lo com um toque, e uma SORT ORDER BY one-pass será realizada de qualquer maneira, como o CBO faz índices função de base não confia o suficiente para permitir que um ORDER BY sobre eles.

Outras dicas

Provavelmente a sua melhor aposta é a de pré-calcular uma coluna e uso separado que para encomendar e usar o número do cliente para exibição. Isso provavelmente envolveria 0-preenchimento quaisquer inteiros internos para um comprimento fixo.

A outra possibilidade é fazer a sua classificação pós-escolha sobre os resultados retornados.

Jeff Atwood montou um blog sobre como algumas pessoas calcular ordens de classificação amigáveis ??humanos.

pode ter uma coluna numérica [CustomerNumberInt] que é utilizado apenas quando o CustomerNumber é puramente numérico (NULL outro modo [1]), em seguida,

ORDER BY CustomerNumberInt, CustomerNumber

[1], dependendo de como seu SQL alças versão nulos em ordem, você pode querer padrão-lo para zero (ou infinito!)

Eu tenho uma situação horrível semelhante e têm desenvolvido uma função adequadamente horrível para lidar com isso (SQLServer)

Na minha situação eu tenho uma tabela de "unidades" (este é um sistema de rastreamento de trabalho para os alunos, de modo unidade neste contexto representa um curso que estão fazendo). As unidades têm um código, que em sua maior parte é puramente numérico, mas por várias razões, foi feito um varchar e eles decidiram prefixo alguns por até 5 caracteres. Então eles esperam 53123237356 para classificar normalmente, mas também T53, T123, T237, T356

unitcode é um nvarchar (30)

Aqui está o corpo da função:

declare @sortkey nvarchar(30)

select @sortkey = 
    case
        when @unitcode like '[^0-9][0-9]%' then left(@unitcode,1) + left('000000000000000000000000000000',30-(len(@unitcode))) + right(@unitcode,len(@unitcode)-1)
        when @unitcode like '[^0-9][^0-9][0-9]%' then left(@unitcode,2) + left('000000000000000000000000000000',30-(len(@unitcode))) + right(@unitcode,len(@unitcode)-2)
        when @unitcode like '[^0-9][^0-9][^0-9][0-9]%' then left(@unitcode,3) + left('000000000000000000000000000000',30-(len(@unitcode))) + right(@unitcode,len(@unitcode)-3)
        when @unitcode like '[^0-9][^0-9][^0-9][^0-9][0-9]%' then left(@unitcode,4) + left('000000000000000000000000000000',30-(len(@unitcode))) + right(@unitcode,len(@unitcode)-4)
        when @unitcode like '[^0-9][^0-9][^0-9][^0-9][^0-9][0-9]%' then left(@unitcode,5) + left('000000000000000000000000000000',30-(len(@unitcode))) + right(@unitcode,len(@unitcode)-5)
        when @unitcode like '%[^0-9]%' then @unitcode
        else left('000000000000000000000000000000',30-len(@unitcode)) + @unitcode
    end 

return @sortkey

Eu queria me atirar na cara depois de escrever que, no entanto, funciona e parece não matar o servidor quando ele é executado.

Eu usei isso no servidor SQL e funcionando muito bem: Aqui a solução é pad os valores numéricos com um personagem na frente para que todos são do mesmo comprimento da corda

.

Aqui está um exemplo usando essa abordagem:

select MyCol
from MyTable
order by 
    case IsNumeric(MyCol) 
        when 1 then Replicate('0', 100 - Len(MyCol)) + MyCol
        else MyCol
    end

O 100 deve ser substituído com o comprimento efectivo da referida coluna.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top