Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Suporte a UTF-8, SQL Server 2012 e UTF8String UDT


Criar um tipo definido pelo usuário personalizado via SQLCLR não , de qualquer forma, vai te dar um substituto de qualquer tipo nativo. É muito útil para criar algo para lidar com dados especializados. Mas strings, mesmo de uma codificação diferente, estão longe de ser especializadas. Seguir esse caminho para seus dados de string destruiria qualquer quantidade de usabilidade do seu sistema, sem mencionar o desempenho, pois você não seria capaz de usar qualquer funções de string embutidas.

Se você pudesse economizar qualquer coisa no espaço em disco, esses ganhos seriam apagados pelo que você perderia no desempenho geral. Armazenar um UDT é feito serializando-o para um VARBINARY . Então, para fazer qualquer comparação de strings OU classificação, fora de uma comparação "binária" / "ordinal", você teria que converter todos os outros valores, um por um, de volta para UTF-8 para fazer a comparação de strings que pode levar em conta as diferenças linguísticas. E essa conversão precisaria ser feita dentro da UDT. Isso significa que, como o tipo de dados XML, você criaria o UDT para manter um valor específico e, em seguida, exporia um método desse UDT para aceitar um parâmetro de string para fazer a comparação (ou seja, Utf8String.Compare(alias.field1) ou, se estiver definindo um operador para o tipo, então Utf8string1 = Utf8string2 e tenha o = operador obtém a string na codificação UTF-8 e, em seguida, faz o CompareInfo.Compare() ).

Além das considerações acima, você também precisa considerar que passar valores de um lado para o outro pela API SQLCLR tem um custo, especialmente ao usar NVARCHAR(MAX) ou VARBINARY(MAX) ao contrário de NVARCHAR(1 - 4000) e VARBINARY(1 - 4000) respectivamente (por favor, não confunda esta distinção como implicando algo sobre o uso de SqlChars / SqlBytes vs SqlString / SqlBinary ).

Finalmente (pelo menos em termos de uso de um UDT), não ignore o fato de que o UDT que está sendo consultado é código de amostra . O único teste observado é puramente funcional, nada sobre escalabilidade ou "lições aprendidas depois de trabalhar com isso por um ano". O código de teste funcional é mostrado aqui na página do CodePlex a seguir e deve ser analisado antes de prosseguir com esta decisão, pois dá uma ideia de como você precisaria escrever suas consultas para interagir com ele (o que é bom para um campo ou dois, mas não para a maioria/todos os campos de string):

http://msftengprodsamples.codeplex.com /SourceControl/latest#Kilimanjaro_Trunk/Programmability/CLR/UTF8String/Scripts/Test.sql

Dado o número de colunas e índices computados persistentes adicionados, algum espaço foi realmente economizado?;-)

Onde o espaço (disco, memória, etc) é a preocupação, você tem três opções:

  1. Se você estiver usando o SQL Server 2008 ou mais recente e estiver no Enterprise Edition, poderá habilitar Compressão de dados . A compactação de dados pode (mas não "sempre") compactar dados Unicode em NCHAR e NVARCHAR Campos. Os fatores determinantes são:
    1. NCHAR(1 - 4000) e NVARCHAR(1 - 4000) use o esquema de compactação padrão para Unicode , mas apenas a partir do SQL Server 2008 R2, E apenas para dados IN ROW, não OVERFLOW! Isso parece ser melhor do que o algoritmo de compactação ROW/PAGE normal.
    2. NVARCHAR(MAX) e XML (e acho que também VARBINARY(MAX) , TEXT e NTEXT ) os dados IN ROW (não fora da linha nas páginas LOB ou OVERFLOW) podem ser compactados pelo menos PAGE e talvez também ROW compactado (não tenho certeza sobre este último).
    3. Quaisquer dados OFF ROW, LOB ou OVERLOW =Sem compactação para você!

  2. Se estiver usando uma versão anterior a 2008 ou não na Enterprise Edition, você poderá ter dois campos:um VARCHAR e um NVARCHAR . Por exemplo, digamos que você está armazenando URLs que são principalmente todos os caracteres ASCII básicos (valores 0 - 127) e, portanto, se encaixam em VARCHAR , mas às vezes têm caracteres Unicode. Seu esquema pode incluir os 3 campos a seguir:
      ...
      URLa VARCHAR(2048) NULL,
      URLu NVARCHAR(2048) NULL,
      URL AS (ISNULL(CONVERT(NVARCHAR([URLa])), [URLu])),
      CONSTRAINT [CK_TableName_OneUrlMax] CHECK (
                        ([URLa] IS NOT NULL OR [URLu] IS NOT NULL)
                    AND ([URLa] IS NULL OR [URLu] IS NULL))
    );
    

    Neste modelo você somente SELECIONE no [URL] coluna computada. Para inserir e atualizar, você determina qual campo usar vendo se a conversão altera o valor de entrada, que deve ser de NVARCHAR modelo:
    INSERT INTO TableName (..., URLa, URLu)
    VALUES (...,
            IIF (CONVERT(VARCHAR(2048), @URL) = @URL, @URL, NULL),
            IIF (CONVERT(VARCHAR(2048), @URL) <> @URL, NULL, @URL)
           );
    

  3. Se você tiver campos que devem ter apenas caracteres que se encaixem em uma página de código específica de um conjunto de caracteres ASCII estendido, use VARCHAR .

P.S. Apenas para esclarecer isso:o novo _SC Os agrupamentos que foram introduzidos no SQL Server 2012 simplesmente permitem:
  • as funções integradas para lidar adequadamente com os caracteres suplementares/pares substitutos e
  • regras linguísticas para caracteres suplementares que são usados ​​para ordenação e comparações

Mas, mesmo sem o novo _SC Collations, você ainda pode armazenar qualquer caractere Unicode em um XML ou N -tipo prefixado e recuperá-lo sem perda de dados. No entanto, ao usar os agrupamentos mais antigos (ou seja, sem número de versão no nome), todos os caracteres suplementares se equivalem. Você precisa usar o _90 e _100 Agrupamentos que, pelo menos, fornecem comparações e classificação de pontos de código / binários; eles não podem levar em conta as regras linguísticas, pois não possuem mapeamentos específicos dos caracteres suplementares (e, portanto, não possuem pesos ou regras de normalização).

Tente o seguinte:
IF (N'𤪆' = N'𤪆') SELECT N'𤪆' AS [TheLiteral], NCHAR(150150) AS [Generated];
IF (N'𤪆' = N'𤪇') SELECT N'𤪇' AS [TheLiteral], NCHAR(150151) AS [Generated];
IF (N'𤪆' COLLATE Tatar_90_CI_AI = N'𤪇' COLLATE Tatar_90_CI_AI)
       SELECT N'𤪇 COLLATE Tatar_90_CI_AI' AS [TheLiteral], NCHAR(150151) AS [Generated];
IF (N'𤪆' = N'?') SELECT N'?';

Em um banco de dados com um agrupamento padrão terminando em _SC , apenas o primeiro IF A instrução retornará um conjunto de resultados e o campo "Gerado" mostrará os caracteres corretamente.

Mas, se o banco de dados não tiver um agrupamento padrão terminando em _SC , e o agrupamento não é um _90 ou _100 agrupamento de séries, então os dois primeiros IF instruções retornam conjuntos de resultados em que o campo "Gerado" retornará NULL , e o campo "Literal" aparece corretamente.

Para dados Unicode, o Collation não afeta o armazenamento físico.

ATUALIZAÇÃO 2018-10-02

Embora ainda não seja uma opção viável, o SQL Server 2019 apresenta suporte nativo para UTF-8 em VARCHAR / CHAR tipos de dados. Existem atualmente muitos bugs com ele para ser usado, mas se eles forem corrigidos, então esta é uma opção para alguns cenários. Por favor, veja minha postagem, "Suporte UTF-8 nativo no SQL Server 2019:Salvador ou Falso Profeta? ", para uma análise detalhada desta nova funcionalidade.