AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 326560
Accepted
Denis Rubashkin
Denis Rubashkin
Asked: 2023-04-28 23:45:41 +0800 CST2023-04-28 23:45:41 +0800 CST 2023-04-28 23:45:41 +0800 CST

Um problema com um caractere em agrupamentos que não diferenciam maiúsculas de minúsculas do Cazaquistão

  • 772

Eu tenho um problema com os caracteres Сyrillic 'E' e 'e', ​​que devem ser iguais quando um agrupamento que não diferencia maiúsculas de minúsculas é usado. E isso é correto para todos os agrupamentos, exceto os cazaques.

Eu verifiquei todos os agrupamentos de CI usando uma consulta abaixo:

SELECT 'DECLARE @Test_' + name + ' TABLE (
    Code nvarchar(32) COLLATE ' + name + '
)

INSERT @Test_' + name + ' (Code)
VALUES (N''Е''), (N''е'')

SELECT ''' + name + ''', * FROM @Test_' + name + ' WHERE Code = N''Е'' COLLATE ' + name 

FROM sys.fn_helpcollations()
WHERE name LIKE '%CI%'

Duas linhas são retornadas conforme o esperado para todos os agrupamentos, mas não para o cazaque.

Exemplo de consulta para ilustrar o problema:

SET NOCOUNT ON;

DECLARE @Test TABLE (
    Code nvarchar(32) COLLATE Kazakh_90_CI_AS
);

DECLARE @UpperChar  nchar(1) = N'Е';
DECLARE @LowerChar  nchar(1) = N'е';

SELECT  ASCII(@UpperChar) AS 'UpperChar ASCII', ASCII(@LowerChar) AS 'LowerChar ASCII';

/* Just ASCII-codes for the chars

UpperChar ASCII LowerChar ASCII
--------------- ---------------
197             229
*/

INSERT @Test (Code)
VALUES (@UpperChar), (@LowerChar);

SELECT DISTINCT Code AS 'DISTINCT Code' FROM @Test;

/* Should be one row with CI collation - FALSE

DISTINCT Code
--------------------------------
Е
е
*/

SELECT Code AS 'Code = @UpperChar'
FROM @Test
WHERE Code = @UpperChar;

/* Should be two rows with CI collation - FALSE

Code = @UpperChar
--------------------------------
Е
*/

SELECT Code AS 'Code = @LowerChar'
FROM @Test 
WHERE Code = @LowerChar;

/* Should be two rows with CI collation - FALSE

Code = @LowerChar
--------------------------------
е
*/

SELECT Code AS 'Code = @UpperChar OR Code = LOWER(@UpperChar)'
FROM @Test
WHERE Code = @UpperChar
    OR Code = LOWER(@UpperChar);

/*Check LOWER('Е') = 'е'  - TRUE

Code = @UpperChar OR Code = LOWER(@UpperChar)
---------------------------------------------
Е
е
*/

SELECT Code AS 'Code = @LowerChar OR Code = UPPER(@LowerChar)'
FROM @Test 
WHERE Code = @LowerChar
    OR Code = UPPER(@LowerChar);

/*Check UPPER('е') = 'Е' - TRUE

Code = @LowerChar OR Code = UPPER(@LowerChar)
---------------------------------------------
Е
е
*/

Outros caracteres Сyrillic se comportam conforme o esperado.

O que eu poderia fazer para corrigir o problema?

sql-server
  • 1 1 respostas
  • 147 Views

1 respostas

  • Voted
  1. Best Answer
    Solomon Rutzky
    2023-05-05T23:23:33+08:002023-05-05T23:23:33+08:00

    Antes de entrarmos nos detalhes, aqui estão duas coisas que ajudariam em geral:

    • A ASCII()função destina-se a VARCHARdados e é sensível à página de código associada ao agrupamento dos dados (para variáveis, é o agrupamento padrão do banco de dados atual). Mas, neste caso, estamos lidando apenas com Unicode/ NVARCHARdados, portanto, a UNICODE()função deve ser usada.
    • Ao trabalhar com caracteres com valores ASCII/pontos de código acima de 127, é útil fazer com que o script crie o(s) caractere(s) específico(s) usando as funções CHAR()ou NCHAR(). Isso torna o script mais transportável, pois não haverá problemas de conversão de caracteres ao abrir/colar o script em um ambiente que não oferece suporte a determinados caracteres. E torna o script mais legível/compreensível, pois não haverá confusão para os leitores ao trabalhar com personagens que se parecem com outros personagens, mas são de fato diferentes (como os que estamos lidando aqui).

    Agora, um pouco de fundo primeiro para que a explicação faça sentido:

    A classificação/comparação Unicode é feita atribuindo vários pesos a cada caractere. Duas das categorias de peso são maiúsculas e minúsculas (isto é, acentos). Ter várias categorias torna mais fácil lidar com as várias combinações de maiúsculas e minúsculas x insensíveis e de acentos x insensíveis. A maioria (se não todos) os caracteres definidos têm pesos de classificação padrão. Esses pesos padrão podem ser substituídos por valores específicos de cultura ao usar uma cultura/localidade específica. Ao usar o inglês dos EUA, os padrões são usados ​​(ou seja, sem substituições). É por isso que os caracteres de outros idiomas ainda são classificados corretamente (ou quase corretamente) mesmo ao usar um Latin1_Generalagrupamento e porque o inglês dos EUA ainda funcionará corretamente ao usar um Hebrew(ou Japanese, etc) agrupamento (porque oHebrewo agrupamento não substitui os pesos dos caracteres do inglês americano).

    Os vários pesos para cada caractere são mantidos em um arquivo. Os mapeamentos de maiúsculas e minúsculas estão em um arquivo separado. E, embora o Consórcio Unicode tenha entrado no ritmo de atualização anual, a Microsoft o faz com menos frequência. Com base em seus arquivos de peso de classificação disponíveis publicamente , eles têm apenas as seguintes versões (para Windows):

    1. Windows NT 4.0 até Windows Server 2003
    2. Windows Vista
    3. WindowsServer 2008
    4. Windows 7 e Windows server 2008 R2
    5. Windows 8 e Windows Server 2012
    6. Windows 10

    O SQL Server tem menos versões:

    1. Os agrupamentos sem um número de versão em seu nome são a versão "80" (ou seja, introduzida no SQL Server 2000)
    2. Collations com _90_em seu nome foram introduzidos no SQL Server 2005
    3. Collations com _100_em seu nome foram introduzidos no SQL Server 2008
    4. Os agrupamentos com _140_o nome foram introduzidos no SQL Server 2017 (infelizmente, os únicos agrupamentos com esta versão são os agrupamentos japoneses)

    Por fim, lembre-se de que os agrupamentos do SQL Server são baseados , mas não são idênticos aos agrupamentos do Windows. Acredito que os agrupamentos da versão 100 estão associados ao arquivo "Windows Server 2008", enquanto os agrupamentos das versões 80 e 90 devem estar mais intimamente associados ao arquivo "Windows NT 4.0 até o Windows Server 2003" (desde que o Vista foi lançado em 2007) .

    Com tudo isso em mente:

    Só para não haver confusão para quem está lendo isso: os caracteres em questão aqui são "Cyrillic Maiúscula Ie: Е" e "Cyrillic Small Letter Ie: е" (pontos de código Unicode U+0415 e U+0435, respectivamente), que parecem idênticos aos caracteres latinos "E" e "e", mas são definitivamente diferentes. Por exemplo:

    SELECT  NCHAR(0x0415) AS [Cyrillic Capital Letter Ie],
            NCHAR(0x0045) AS [Latin Capital Letter E],
            IIF(NCHAR(0x0415) = NCHAR(0x0045) COLLATE Kazakh_100_CI_AI, '=', '<>') AS [Kazakh],
            IIF(NCHAR(0x0415) = NCHAR(0x0045) COLLATE Latin1_General_100_CI_AI, '=', '<>')
              AS [Latin1_General]
    

    retorna:

    Cyrillic Capital Letter Ie    Latin Capital Letter E    Kazakh    Latin1_General
    Е                             E                         <>        <>
    

    O comportamento padrão em todos os arquivos de peso de classificação da Microsoft é que esses dois caracteres são os mesmos, exceto pelo caso. É por isso que eles comparam como iguais em todos os agrupamentos que não diferenciam maiúsculas de minúsculas, exceto os Kazakhagrupamentos.

    Por que esses dois caracteres — U+0415 e U+0435 — são comparados como diferentes ao usar agrupamentos cazaques que não diferenciam maiúsculas de minúsculas? Porque (e por qualquer motivo), os arquivos de peso de classificação "Windows NT 4.0 até Windows Server 2003" e "Windows Server 2008" contêm uma substituição para U+0435 ("Cyrillic Small Letter Ie: е") ao usar o cazaque cultura. A substituição faz com que este caractere, U+0435, seja igual aos seguintes caracteres (e alguns outros):

    -- case-sensitive
    SELECT  NCHAR(0x0435) AS [Cyrillic Small Letter Ie], -- е
            NCHAR(0x04bf) AS [Cyrillic Small Letter Abkhasian Che With Descender], -- ҿ
            IIF(NCHAR(0x0435) = NCHAR(0x04bf) COLLATE Kazakh_90_CS_AI, '=', '<>') AS [Kazakh_90], -- =
            IIF(NCHAR(0x0435) = NCHAR(0x04bf) COLLATE Kazakh_100_CS_AI, '=', '<>') AS [Kazakh_100], -- =
            IIF(NCHAR(0x0435) = NCHAR(0x04bf) COLLATE Latin1_General_CS_AI, '=', '<>') AS [Latin1_General], -- =
            IIF(NCHAR(0x0435) = NCHAR(0x04bf) COLLATE Latin1_General_100_CS_AI, '=', '<>') AS [Latin1_General_100] -- <>
    
    -- accent-sensitive
    SELECT  NCHAR(0x0435) AS [Cyrillic Small Letter Ie], -- е
            NCHAR(0x0404) AS [Cyrillic Capital Letter E], -- Є
            IIF(NCHAR(0x0435) = NCHAR(0x0404) COLLATE Kazakh_90_CI_AS, '=', '<>') AS [Kazakh_90],-- <>
            IIF(NCHAR(0x0435) = NCHAR(0x0404) COLLATE Kazakh_100_CI_AS, '=', '<>') AS [Kazakh_100], -- =
            IIF(NCHAR(0x0435) = NCHAR(0x0404) COLLATE Latin1_General_CI_AS, '=', '<>') AS [Latin1_General],-- <>
            IIF(NCHAR(0x0435) = NCHAR(0x0404) COLLATE Latin1_General_100_CI_AS, '=', '<>') AS [Latin1_General_100]-- <>
    
    -- case and accent -sensitive
    SELECT  NCHAR(0x0435) AS [Cyrillic Small Letter Ie], -- е
            NCHAR(0x0454) AS [Cyrillic Small Letter E], -- є
            IIF(NCHAR(0x0435) = NCHAR(0x0454) COLLATE Kazakh_90_CS_AS, '=', '<>') AS [Kazakh_90], -- <>
            IIF(NCHAR(0x0435) = NCHAR(0x0454) COLLATE Kazakh_100_CS_AS, '=', '<>') AS [Kazakh_100], -- =
            IIF(NCHAR(0x0435) = NCHAR(0x0454) COLLATE Latin1_General_CS_AS, '=', '<>') AS [Latin1_General], -- <>
            IIF(NCHAR(0x0435) = NCHAR(0x0454) COLLATE Latin1_General_100_CS_AS, '=', '<>') AS [Latin1_General_100] -- <>
    

    As substituições específicas do Cazaquistão também fazem com que o caractere "Cyrillic Small Letter Io" (U+0451) não seja mais igual ao seu caractere maiúsculo:

    -- Also messed up, just like Ie; accent-sensitive
    SELECT  NCHAR(0x0451) AS [Cyrillic Small Letter Io], -- ё
            NCHAR(0x0401) AS [Cyrillic Capital Letter Io], -- Ё
            IIF(NCHAR(0x0451) = NCHAR(0x0401) COLLATE Kazakh_90_CI_AS, '=', '<>') AS [Kazakh_90],-- <>
            IIF(NCHAR(0x0451) = NCHAR(0x0401) COLLATE Kazakh_100_CI_AS, '=', '<>') AS [Kazakh_100], -- <>
            IIF(NCHAR(0x0451) = NCHAR(0x0401) COLLATE Latin1_General_CI_AS, '=', '<>') AS [Latin1_General],-- =
            IIF(NCHAR(0x0451) = NCHAR(0x0401) COLLATE Latin1_General_100_CI_AS, '=', '<>') AS [Latin1_General_100]-- =
    

    Todo esse comportamento é remanescente da implementação inicial do Unicode da Microsoft (lá no Windows NT 4.0!). A Microsoft, para seu crédito, foi uma das primeiras a adotar o Unicode, e esse comportamento podeser como foi definido no Unicode versão 1.0. É difícil dizer com certeza, pois não é fácil encontrar os arquivos de agrupamento originais para Unicode (acho que a versão 2.1 foi a mais antiga que pude encontrar). No entanto, posso dizer, observando os arquivos fornecidos pela Microsoft, embora eles tenham atualizado os pesos de classificação padrão e adicionado caracteres e culturas em cada um desses arquivos, algumas definições, como as substituições específicas do Cazaquistão, não foram atualizadas até mais recentemente. . Na verdade, não é até o arquivo "Windows 8 e Windows Server 2012" (ou seja, o segundo conjunto de definições mais recente) que eles corrigem as substituições específicas do Cazaquistão (e presumo que outras).

    Portanto, embora o Windows em geral (e possivelmente também o .NET) manipule corretamente os agrupamentos do Cazaquistão (começando com o Windows 8 e o Windows Server 2012), os agrupamentos do SQL Server ainda estão presos no passado. Ou seja, não há correção fora de um milagre que a Microsoft atualize esses agrupamentos para uma versão mais recente do Unicode. Eu tenho uma ideia que estou pensando em propor a eles como torná-la ainda melhor do que uma simples atualização de definição, então talvez eu deva prosseguir e enviar isso (atualizarei esta resposta com o link assim que terminar que).

    Mas, por enquanto, se você realmente precisa que esses dois caracteres sejam iguais em um agrupamento cazaque sem distinção entre maiúsculas e minúsculas (e não se esqueça de "Cyrillic Small Letter Io" (U+0451)), você terá que fazer o truque do seu UPPER()exemplo (embora eu não possa dizer com certeza que essa solução alternativa não causará nenhum problema).


    Para obter mais informações sobre agrupamentos, Unicode e codificações, visite meu site: Collations.Info

    • 3

relate perguntas

  • SQL Server - Como as páginas de dados são armazenadas ao usar um índice clusterizado

  • Preciso de índices separados para cada tipo de consulta ou um índice de várias colunas funcionará?

  • Quando devo usar uma restrição exclusiva em vez de um índice exclusivo?

  • Quais são as principais causas de deadlocks e podem ser evitadas?

  • Como determinar se um Índice é necessário ou necessário

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve