Como classificar e exibir listas mistas de alfas e números como os usuários esperam?
-
23-08-2019 - |
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 CustomerNumber
s). 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?
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:.
-
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.