Como o agrupamento sem distinção entre maiúsculas e minúsculas funciona?

19

O tipo de agrupamento padrão no SQL Server permite a indexação em relação a seqüências que não diferenciam maiúsculas de minúsculas, mas o caso dos dados é persistente. Como isso realmente funciona? Estou procurando as porcas e parafusos reais, bits e bytes, ou um bom recurso que explica isso em detalhes.

create table casetest (fruitnames nvarchar(50) not null);
create unique index IX_fruitnames on casetest(fruitnames);

insert into casetest values ('apples');
insert into casetest values ('Pears');
-- this insert fails
insert into casetest values ('pears');

-- this yields 'Pears' as a result
select * from casetest (forceseek) where fruitnames = 'PEARS'

update casetest set fruitnames = 'pears' where fruitnames = 'pEArs'

-- this yields 'pears' as a result
select * from casetest (forceseek) where fruitnames = 'PEARS'

Perguntas sobre agrupamentos do SQL Server que você era muito tímido para perguntar por Robert Sheldon aborda como usar o agrupamento. Não cobre como o agrupamento funciona. Estou interessado em saber como um índice pode ser criado / consultado com eficiência, sem se importar com o caso, enquanto simultaneamente armazena dados do caso.

cocogorilla
fonte
11
Você pode consultar com eficiência (por exemplo, utilizando uma busca por índice) seqüências que não diferenciam maiúsculas de minúsculas em um campo que diferencia maiúsculas de minúsculas, mas é um pouco irritante .
John Eisbrener 26/09
cocogorilla: por favor, veja a nota nº 1 que acabei de adicionar ao final da minha resposta: re-o agrupamento "padrão".
Solomon Rutzky

Respostas:

26

indexação em relação a cadeias sem distinção entre maiúsculas e minúsculas, mas o caso dos dados é persistente. Como isso realmente funciona?

Na verdade, esse não é um comportamento específico do SQL Server, é apenas como essas coisas funcionam em geral.

Então, os dados são os dados. Se você estiver falando especificamente sobre um índice, os dados precisarão ser armazenados, pois, caso contrário, seria necessária uma consulta na tabela principal para obter o valor real e não haveria possibilidade de um índice de cobertura (em menos não para tipos de string).

Os dados, na tabela / índice agrupado ou não agrupado, não contêm nenhuma informação de agrupamento / classificação. São simplesmente dados. O agrupamento (regras de localização / cultura e sensibilidades) são apenas metadados anexados à coluna e usados ​​quando uma operação de classificação é chamada (a menos que substituída por umCOLLATEcláusula), que incluiria a criação / reconstrução de um índice. As regras definidas por um agrupamento não binário são usadas para gerar chaves de classificação, que são representações binárias da cadeia de caracteres (as chaves de classificação são desnecessárias em agrupamentos binários). Essas representações binárias incorporam todas as regras de localidade / cultura e sensibilidades selecionadas. As chaves de classificação são usadas para colocar os registros em sua ordem correta, mas não são elas próprias armazenadas no índice ou na tabela. Eles não são armazenados (pelo menos eu não vi esses valores no índice e foi informado que eles não são armazenados) porque:

  1. Eles não são realmente necessários para classificação, pois estariam apenas na mesma ordem que as linhas da tabela ou do índice. Mas, a ordem física do índice é apenas uma classificação, não uma comparação.
  2. Embora armazená-las possa tornar as comparações mais rápidas, também aumentaria o índice, pois o tamanho mínimo para um único caractere é de 5 bytes, e isso é apenas "sobrecarga" (da estrutura da chave de classificação). A maioria dos caracteres tem 2 bytes cada, mais 1 byte se houver acento, mais 1 byte se estiver em maiúsculas. Por exemplo, "e" é uma chave de 7 bytes, "E" e "é" são ambos 8 bytes e "É" é uma chave de 9 bytes. Portanto, não vale a pena armazená-los no final.

Existem dois tipos de agrupamentos: SQL Server e Windows.

servidor SQL

Os agrupamentos do SQL Server (aqueles com nomes começando com SQL_) são a maneira mais antiga de classificação / comparação anterior ao SQL Server 2000 (embora aindaSQL_Latin1_General_CP1_CI_AS seja o padrão de instalação nos sistemas operacionais em inglês dos EUA, infelizmente). Nesse modelo mais antigo, simplista e não Unicode, cada combinação de código de idioma, página de códigos e várias sensibilidades recebe um mapeamento estático de cada um dos caracteres nessa página de códigos. Cada caractere recebe um valor (ou seja, peso da classificação) para indicar como ele se iguala aos outros. As comparações neste modelo parecem fazer uma operação de duas passagens:

  1. Primeiro, ele remove todos os sotaques (como "  ü  " se torna "  u  "), expande caracteres como "  Æ  " para "  A  " e "  E  " e, em seguida, faz uma classificação inicial para que as palavras estejam em uma ordem natural (como você faria espere encontrá-los em um dicionário).
  2. Em seguida, ele vai caractere por caractere para determinar a igualdade com base nesses valores subjacentes para cada caractere. Esta segunda parte é o que mustaccio está descrevendo em sua resposta .

As únicas sensibilidades que podem ser ajustadas nessas intercalações são: "case" e "sotaque" ("width", "tipo de kana" e "seletor de variação" não estão disponíveis). Além disso, nenhum desses agrupamentos oferece suporte a caracteres suplementares (o que faz sentido, pois eles são específicos para Unicode e esses agrupamentos se aplicam apenas a dados não Unicode).

Essa abordagem se aplica apenas a VARCHARdados não Unicode . Cada combinação exclusiva de localidade, página de código, distinção entre maiúsculas e minúsculas e distinção entre caracteres tem um "ID de classificação" específico, que você pode ver no exemplo a seguir:

SELECT COLLATIONPROPERTY(N'SQL_Latin1_General_CP1_CI_AS', 'SortID'), -- 52
       COLLATIONPROPERTY(N'SQL_Latin1_General_CP1_CS_AS', 'SortID'), -- 51
       COLLATIONPROPERTY(N'Latin1_General_100_CI_AS',     'SortID'); --  0

A única diferença entre os dois primeiros agrupamentos é a distinção entre maiúsculas e minúsculas. O terceiro agrupamento é um agrupamento do Windows e, portanto, não possui uma tabela de mapeamento estática.

Além disso, esses agrupamentos devem classificar e comparar mais rapidamente do que os agrupamentos do Windows devido à simples pesquisa de caracteres para classificar o peso. No entanto, esses agrupamentos também são muito menos funcionais e geralmente devem ser evitados, se possível.

janelas

Os agrupamentos do Windows (aqueles com nomes que não começam SQL_) são a maneira mais nova (começando no SQL Server 2000) de classificar / comparar. Nesse modelo Unicode mais novo e complexo, cada combinação de código do idioma, página de códigos e várias sensibilidades não recebe um mapeamento estático. Por um lado, não há páginas de código neste modelo. Esse modelo atribui um valor de classificação padrão a cada caractere e, em seguida, cada localidade / cultura pode atribuir novamente os valores de classificação a qualquer número de caracteres. Isso permite que várias culturas usem os mesmos caracteres de maneiras diferentes. Isso tem o efeito de permitir que vários idiomas sejam classificados naturalmente usando o mesmo agrupamento se eles não usarem os mesmos caracteres (e se um deles não precisar atribuir novamente nenhum valor e simplesmente usar os padrões).

Os valores de classificação neste modelo não são valores únicos. Eles são uma matriz de valores que atribuem pesos relativos à letra base, quaisquer sinais diacríticos (por exemplo, acentos), letras maiúsculas e minúsculas etc. Se o agrupamento faz distinção entre maiúsculas e minúsculas, a parte "maiúscula" dessa matriz é usada, caso contrário, ela será ignorada ( portanto, insensível). Se o agrupamento for sensível ao acento, a parte "diacrítica" da matriz será usada, caso contrário, será ignorada (portanto, insensível).

As comparações neste modelo são uma operação de várias passagens:

  1. Primeiro, a string é normalizada para que várias maneiras de representar o mesmo caractere sejam iguais. Por exemplo, " ü " poderia ser um único caractere / ponto de código (U + 00FC). Você também pode combinar um " u " não acentuado (U + 0075) com uma Diérese Combinada " ̈ " (U + 0308) para obter: " ü ", que não apenas parece o mesmo quando renderizado (a menos que haja um problema com sua fonte), mas também é considerado o mesmo que a versão de caractere único (U + 00FC), a menos que seja utilizado um agrupamento binário (que compara bytes em vez de caracteres). A normalização divide o caractere único em várias partes, o que inclui expansões para caracteres como "  Æ  " (conforme observado acima para agrupamentos do SQL Server).
  2. A operação de comparação neste modelo passa caracter por caractere a cada sensibilidade . As chaves de classificação para as seqüências de caracteres são determinadas aplicando os elementos apropriados de cada matriz de agrupamento de caracteres, com base nos quais as sensibilidades são "sensíveis". Os valores da chave de classificação são organizados por todas as sensibilidades primárias de cada caractere (o caractere base), seguidas por todas as sensibilidades secundárias (peso diacrítico), seguidas pelo peso da caixa de cada caractere e assim por diante.
  3. A classificação é realizada com base nas chaves de classificação calculadas. Com cada sensibilidade agrupada, é possível obter uma ordem de classificação diferente da obtida com um agrupamento equivalente do SQL Server ao comparar seqüências de caracteres de vários caracteres e acentos envolvidos, e o agrupamento é sensível a acentos (e mais ainda se o agrupamento for também diferencia maiúsculas de minúsculas).

Para obter mais detalhes sobre essa classificação, publicarei uma postagem que mostre os valores das chaves de classificação, como eles são calculados, as diferenças entre agrupamentos do SQL Server e do Windows, etc. Mas, por enquanto, consulte minha resposta para: Classificação sensível ao acento ( observe que a outra resposta a essa pergunta é uma boa explicação do algoritmo oficial Unicode, mas o SQL Server usa um algoritmo personalizado, embora semelhante, e até uma tabela de ponderação personalizada).

Todas as sensibilidades podem ser ajustadas nesses agrupamentos: "maiúsculas e minúsculas", "acento", "largura", "tipo de kana" e "seletor de variação" (a partir do SQL Server 2017 e apenas para os agrupamentos em japonês). Além disso, alguns desses agrupamentos (quando usados ​​com dados Unicode) oferecem suporte a caracteres suplementares (a partir do SQL Server 2012). Essa abordagem se aplica aos dados NVARCHAR e VARCHAR (mesmo que não sejam Unicode). Aplica-se a VARCHARdados não Unicode , primeiro convertendo o valor internamente em Unicode e aplicando as regras de classificação / comparação.


Observe:

  1. Não há agrupamento padrão universal para o SQL Server. Há um padrão de instalação que difere com base na configuração atual de idioma / idioma do sistema operacional no momento da instalação (que infelizmente é SQL_Latin1_General_CP1_CI_ASpara sistemas em inglês dos EUA, então vote nesta sugestão ). Isso pode ser alterado durante a instalação. Esse agrupamento no nível da instância define o agrupamento para o [model]banco de dados, que é o modelo usado ao criar novos bancos de dados, mas o agrupamento pode ser alterado durante a execução CREATE DATABASE, especificando a COLLATEcláusula. Esse agrupamento no nível do banco de dados é usado para literais de variáveis ​​e seqüências de caracteres, bem como o padrão para novas colunas (e alteradas!) Quando a COLLATEcláusula não é especificada (que é o caso do código de exemplo na pergunta).
  2. Para obter mais informações sobre agrupamentos / codificações / Unicode, visite: Informações sobre agrupamentos
Solomon Rutzky
fonte
5

Normalmente, isso é implementado usando tabelas de agrupamento que atribuem uma certa pontuação a cada personagem. A rotina de classificação possui um comparador que usa uma tabela apropriada, padrão ou especificada explicitamente, para comparar cadeias, caractere por caractere, usando suas pontuações de ordenação. Se, por exemplo, uma tabela de intercalação específica atribuir uma pontuação de 1 a "a" e 201 a "A", e uma pontuação mais baixa nessa implementação específica significar maior precedência, então "a" será classificado antes de "A". Outra tabela pode atribuir pontuações reversas: 201 a "a" e 1 a "A", e a ordem de classificação será subsequentemente inversa. Ainda outra tabela pode atribuir pontuações iguais a "a", "A", "Á" e "Å", o que levaria a uma comparação e classificação sem distinção entre maiúsculas e minúsculas.

Da mesma forma, um comparador baseado em tabela de intercalação usado ao comparar uma chave de índice com o valor fornecido no predicado.

mustaccio
fonte
11
Apenas para sua informação pessoal: essas informações estão corretas apenas no uso de agrupamentos do SQL Server (ou seja, aqueles com nomes começando com SQL_) quando usados ​​nos VARCHARdados. Isso não é exatamente verdadeiro para NVARCHARdados ou VARCHARdados ao usar um agrupamento do Windows (nomes que não começam com SQL_).
Solomon Rutzky 26/09