Existe um agrupamento para classificar as seguintes cadeias na seguinte ordem 1,2,3,6,10,10A, 10B, 11?

12

Eu tenho um banco de dados com uma coluna VARCHAR que contém números inteiros de comprimento variável. Quero classificá-los para que 10 venha após 9, não 1, e 70A venha após 70. Consegui fazer isso com as instruções PATINDEX () , CTE e CASE na cláusula WHERE.

No entanto, eu queria saber se havia um agrupamento onde isso seria desnecessário.

Justin Dearing
fonte
Aqui está o novo link para essa sugestão agora que Microsoft migrou do Connect para UserVoice ainda não fez avançar os URIs: Suporte "ordenação natural" / DIGITSASNUMBERS como uma opção de Agrupamento
Solomon Rutzky
2
A Microsoft disse que implementará isso como um recurso interno do SQL Server se obtiverem votos suficientes. Então vá aqui e clique no botão votar .
precisa

Respostas:

8

Não. O agrupamento refere-se à classificação alfabética, dependendo da página de código, acento, caixa, largura e kana. Os caracteres numéricos (0 a 9) não possuem propriedades.

Assim 9é sempre depois 10Bde qualquer tipo.

Você deve dividir como anotou ou classificar assim:

ORDER BY
    RIGHT('                              ' + MyColumn, 30)

O comprimento à direita determina quantos espaços você tem.

Você poderia, é claro:

  • tem 2 colunas para tornar isso desnecessário (e muito mais rápido) e tem uma coluna computada para combiná-las
  • insista em zeros à esquerda
  • justificar à direita em um caractere (uma versão armazenada do meu DIREITO acima)

As duas últimas sugestões são como o meu DIREITO acima e um pouco diferente. Classificação mais rápida (não é necessário processamento do colukmn), mas é necessário mais armazenamento

gbn
fonte
Não vejo como isso funciona. Ele quebra para 2, 2a, 3, etc ...
Mladen Prajdic
@Mladen Prajdic: você está correto, oops. Esqueceu-se sobre as alphabeticals arrasto
GBN
Em relação a " Assim 9é sempre depois 10Bde qualquer tipo ": é apenas dessa maneira no SQL Server porque a opção de classificação subjacente para manipular "DigitsAsNumbers" não foi exposta como uma opção de Agrupamento. Ainda ;-). Isso ficou disponível para aplicativos baseados no Windows a partir do Windows 7, principalmente no File Explorer. E pode um dia ser exposto ao SQL Server, se pessoas suficientes apoiarem a ideia. Tentei fazer a bola rolar preenchendo a seguinte sugestão do Connect: Suporte "classificação natural" / DIGITSASNUMBERS como uma opção de agrupamento .
Solomon Rutzky
8

Eu configuraria uma coluna computada e depois classificaria com base nisso. Algo como

CAST( 
     CASE WHEN IS_NUMERIC(left(OtherColumn, 2) = 1) then 
         left(OtherColumn,2) 
     else 
         left(otherColumn, 1)  
AS INT)

Em seguida, use esta coluna para classificar, pois agora você pode indexar a coluna.

mrdenny
fonte
Isso é realmente útil para saber sobre problemas semelhantes. No entanto, neste caso, não posso alterar o esquema.
Justin Dearing
Você pode adicionar ao esquema? Com exceção de uma coluna computada, você sempre pode criar uma visualização - embora isso não seja realmente otimizável como uma coluna calculada.
Aaron Bertrand
Se você fizer uma exibição indexada e tiver a edição Enterprise, sua consulta usará a exibição indexada automaticamente se puder descobrir o que você está tentando fazer. Se a edição padrão você precisaria usar WITH (NOEXPAND) para acionar a exibição indexada a ser usada. Nesse ponto, você precisaria ter a declaração do caso em seu pedido, mas deve funcionar, eu acho.
22811 jrdenny
Você não precisa criar uma coluna computada. Você pode usar essa expressão diretamente na cláusula ORDER BY
a_horse_with_no_name
Se você deseja garantir uma verificação de índice ou tabela, pode fazê-lo. Se você deseja poder indexar o valor, é necessária uma coluna calculada ou uma exibição indexada.
mrdenny
5

Se você quiser uma maneira dolorosa de provar o que o @gbn está dizendo (essencialmente que você não pode dizer a um agrupamento para ordenar substrings de maneira diferente), crie uma tabela rápida #temp que tenha um coeficiente para o pedido que você espera e veja se ordenar por qualquer agrupamento retorna a mesma ordem:

CREATE TABLE #foo(id INT, n NVARCHAR(10));

CREATE TABLE #bar(collation SYSNAME);

SET NOCOUNT ON;

INSERT #foo SELECT 1,'1'
UNION SELECT 2,'2'
UNION SELECT 3,'3'
UNION SELECT 4,'6'
UNION SELECT 5,'10'
UNION SELECT 6,'10A'
UNION SELECT 7,'10B'
UNION SELECT 8,'11';

DECLARE @sql NVARCHAR(MAX) = N'';

SELECT @sql += N'
    WITH x AS 
    (
        SELECT n, rn = ROW_NUMBER() OVER 
        (ORDER BY n COLLATE ' + name + ') FROM #foo
    ) 
    INSERT #bar 
    SELECT TOP (1) ''' + name + ''' FROM x
    WHERE NOT EXISTS
    (
        SELECT COUNT(*) FROM #foo AS f
        WHERE f.id = x.rn
        AND f.n <> x.n
    );' FROM sys.fn_helpcollations();

EXEC sp_executesql @sql;

SELECT collation FROM #bar;

GO
DROP TABLE #foo, #bar;

Isso funciona para mim em cerca de 10 segundos e produz 0 linhas - o que significa que nenhum agrupamento disponível para o SQL Server (pelo menos 2008 R2, ainda não experimentou o Denali) será classificado da maneira que você espera. Você precisa de uma maneira diferente de definir a classificação.

Aaron Bertrand
fonte
2

Deseja um meio sensato e eficiente de classificar números em strings como números reais? Considere votar na minha sugestão do Microsoft Connect: Suporte "classificação natural" / DIGITSASNUMBERS como uma opção de agrupamento


Embora esta pergunta seja específica do SQL Server e essa resposta não, achei que ainda deveria postar essas informações simplesmente para conscientizá-las e não para opor-me a qualquer outra resposta.

Dito isto, fora do SQL Server, em certos ambientes, é possível fazer esse tipo de classificação. É algo que é pelo menos especificado na documentação Unicode. No padrão / relatório UNICODE LOCALE MARKUP LANKUAGE (LDML) PARTE 5: COLLATION , há um gráfico para Configurações de agrupamento que descreve várias opções para personalizar o comportamento de classificação. Uma das opções é -kn-trueou [numericOrdering on]:

Se o conjunto de sobre , qualquer sequência de dígitos decimais (General_Category = Nd no [ UAX44 ]) é ordenada a um nível primário com o seu valor numérico. Por exemplo, "A-21" <"A-123". Os pesos primários calculados estão todos no início do grupo de reordenação de dígitos . Assim, com uma tabela UCA não encadernada, "a $" <"a0" <"a2" <"a12" <"a⓪" <"aa".

No entanto, este documento é um "padrão técnico" e não faz parte da especificação principal do Unicode. Uma observação na parte superior do documento afirma:

Um Padrão Técnico Unicode (UTS) é uma especificação independente. A conformidade com o Padrão Unicode não implica conformidade com nenhum UTS.

Portanto, esse comportamento específico não está disponível no SQL Server ou no .NET (pelo menos não de forma nativa), embora ambos estejam em conformidade com a especificação principal do Unicode.

O projeto ICU (International Components for Unicode) é um conjunto de bibliotecas C / C ++ e Java que implementa essa funcionalidade, e há até uma demonstração on-line. E em "projetos relacionados", há um link para um projeto .NET que parece ser um invólucro de objeto COM para a biblioteca ICU que permitiria que essa funcionalidade fosse exposta ao código gerenciado. Mas não está claro se esse projeto .NET ainda está ativo.

Mas, para ver esse comportamento em ação, vá para a Demonstração de agrupamento da UTI .

Cole o seguinte na área de texto Entrada , no lado esquerdo:

1
2
10B
6
11
10A
3
10

Defina todas as opções para "padrão". Marque a opção "inserir números de linha" à direita do sortbotão e verifique se a opção "pontos fortes da diferença" está desmarcada.

Clique no sortbotão e você deverá recuperar o seguinte:

[1] 1
[8] 10
[6] 10A
[3] 10B
[5] 11
[2] 2
[7] 3
[4] 6

É o que se deve esperar ao fazer uma classificação típica de string e o que você está vendo no SQL Server.

Agora, na série de botões de opção logo acima do sortbotão, a segunda linha é rotulada como "numérica". Selecione o botão de opção "ativado".

Clique no sortbotão novamente e você deverá recuperar o seguinte:

[1] 1
[2] 2
[7] 3
[4] 6
[8] 10
[6] 10A
[3] 10B
[5] 11

Questionando se isso funciona quando a parte numérica está no meio da string? Ok, cole o seguinte na área de texto Entrada no lado esquerdo (substituindo a lista anterior):

Script - 1.sql
Script - 2.sql
Script - 10B.sql
Script - 6.sql
Script - 11.sql
Script - 10A.sql
Script - 3.sql
Script - 10.sql

Verifique se a configuração numérica ainda está definida como "ativada". Clique no sortbotão novamente e você deverá recuperar o seguinte:

[1] Script - 1.sql
[2] Script - 2.sql
[7] Script - 3.sql
[4] Script - 6.sql
[8] Script - 10.sql
[6] Script - 10A.sql
[3] Script - 10B.sql
[5] Script - 11.sql

Deseja ver isso em outro lugar? Crie uma pasta no seu disco rígido, algo como C: \ temp \ sorting \ , e crie arquivos vazios com os mesmos nomes "Script -...". Faça um DIRem uma janela de comando e você verá a classificação padrão. Mas, ao examinar a lista de arquivos no Windows Explorer, você verá a lista classificada usando a opção "numérico" :-).

Solomon Rutzky
fonte
Para sua informação, o Postgres 10 obtém suporte para agrupamentos na UTI. Veja este post de Peter Eisentraut.
Basil Bourque
@BasilBourque Obrigado por mencionar isso sobre o PG10. No final, essa postagem do blog afirma que "a ICU oferece muitas funcionalidades nessa área que ainda não estamos expondo pelo PostgreSQL. Existem opções para classificação sem distinção entre maiúsculas e minúsculas, classificação sem distinção de sotaque e personalização total de um agrupamento. Veja para aqueles em versões futuras do PostgreSQL. " Portanto, em sua primeira / atual implementação, ela não altera nenhuma das informações na minha resposta. Se uma oferta futura permitir a classificação numérica, eu a mencionarei na minha resposta, mas como uma nota de rodapé, pois essa pergunta é específica do SQL Server.
Solomon Rutzky