Por que o tipo de dados varchar permite valores unicode?

17

Eu tenho uma tabela com uma coluna varchar. Ele permite marcas comerciais (™), direitos autorais (©) e outros caracteres Unicode, como mostrado abaixo.

Create table VarcharUnicodeCheck
(
col1 varchar(100)
)

insert into VarcharUnicodeCheck (col1) values ('MyCompany')
insert into VarcharUnicodeCheck (col1) values ('MyCompany™')
insert into VarcharUnicodeCheck (col1) values ('MyCompany░')
insert into VarcharUnicodeCheck (col1) values ('MyCompanyï')
insert into VarcharUnicodeCheck (col1) values ('MyCompany')

select * from VarcharUnicodeCheck

Mas a definição de varchar diz que permite dados de string não unicode. Mas os símbolos de marca comercial (™) e registrada (®) são caracteres Unicode . A definição contradiz a propriedade do tipo de dados varchar? Eu li alguns links como o primeiro e o segundo . Mas ainda não consegui entender por que ele permite string unicode quando a definição diz que permite apenas valores de string não unicode.

Shiva
fonte
12
Todos os caracteres são caracteres Unicode.
Martin Smith
A Microsoft geralmente usa UNICODE quando se refere a UTF-16 / UCS-2. Portanto, eles podem nem contar UTF-8, pois UNICODE é um contexto.
CodesInChaos 30/01
1
@CodesInChaos: lutei para analisar seu comentário, mas temo que você esteja confundindo Unicode com as várias codificações UTF-n.
Lightness Races com Monica
1
@ Martin Smith: Se todos os caracteres são caracteres Unicode, por que a definição varchar da Microsoft diz que permite dados de seqüência de caracteres não Unicode?
Shiva
2
a codificação dos caracteres em VARCHAR não é Unicode mas todos os caracteres existir em Unicode
Martin Smith,

Respostas:

15

Mas os símbolos de marca comercial (™) e registrada (®) são caracteres Unicode.

Você está errado aqui. Suas seqüências contêm apenas asciicaracteres.

Aqui está um teste simples que mostra que seus personagens são todos ascii (+ alguns extended asciicom códigos ascii entre 128 e 255):

declare @VarcharUnicodeCheck table
(
col1 varchar(100)
)

insert into @VarcharUnicodeCheck (col1) values ('MyCompany')
insert into @VarcharUnicodeCheck (col1) values ('MyCompany™')
insert into @VarcharUnicodeCheck (col1) values ('MyCompany░')
insert into @VarcharUnicodeCheck (col1) values ('MyCompanyï')
insert into @VarcharUnicodeCheck (col1) values ('MyCompany')

select *,
        right(col1, 1)as last_char, 
        ascii(right(col1, 1)) as_last_char_ascii
from @VarcharUnicodeCheck;

Aqui você pode ver claramente que todos os seus personagens são codificados em 1 byte:

insira a descrição da imagem aqui

Sim, eles não são caracteres ascii puros, mas são ASCII estendidos .

Aqui eu mostro o caractere unicode real Trademark(™)e seu código e representação binária:

declare @t table (uni_ch nchar(1), ascii_ch char(1));
insert into @t values (N'™', '™');

select unicode(uni_ch) as [unicode of ™], 
       ascii(ascii_ch) [ascii of ™], 
       cast(uni_ch as varbinary(10)) as [uni_ch as varbinary], 
       cast(ascii_ch as varbinary(10)) as [ascii_ch as varbinary]
from @t;

insira a descrição da imagem aqui

Finalmente, você pode ver que o Trademark(™)caractere unicode tem código 8482 e não 153:

select nchar(8482), nchar(153)
sepupico
fonte
1
Mas não há uma palavra "ASCII" no artigo que você mencionou, eles estão falando apenas de caracteres unicode e não unicode, e a Trademark (™) que você usou não era unicode.
Sepupic
16
"ASCII estendido" é um termo terrivelmente ambíguo. Seria mais útil analisar qual codificação de 8 bits é realmente usada (é baseada nas configurações de local / agrupamento?). Eu estou supondo que a página de código 1252 do Windows , que realmente codifica ™ como caractere 153.
IMSoP 30/01
2
@ Sepupic Acho que você precisa ler mais sobre a diferença entre códigos e codificações. Wikipedia pode ajudar. "Uma codificação mapeia (possivelmente um subconjunto) do intervalo de código Unicode aponta para seqüências de valores em algum intervalo de tamanho fixo, denominados valores de código ". 8482 é o codepoint para ™, que pode ser codificado como \ x99 (153) no Windows-1252, como \ Xaa na MacRoman, como \ XE2 \ x84 \ xa2 em UTF-8, etc.
curiousdannii
7
Deve-se tomar cuidado com caracteres de 8 bits acima de 127: o que cada código acima de 127 representa pode e será alterado, dependendo da codificação em uso, que variará dependendo do agrupamento em uso. Na página de código 1252, o unicode 8482 é mapeado para 153. Na página de código 850, esse ponto é ocupado por 214 ( Ö) e na ISO-8859-1 (às vezes chamada de Latin1) é um código de controle sem representação imprimível. A menos que você saiba que sempre usará a mesma página de código, é mais seguro aderir a caracteres ANSI (127 ou menos) ou usar tipos Unicode. A página de código 1252 é mais comum no SQL Server, mas está longe de ser onipresente.
David Spillett
4
@Shiva O Mínimo Absoluto Todo desenvolvedor de software deve saber absolutamente, positivamente sobre Unicode e conjuntos de caracteres . ASCII é um subconjunto de muitas codificações, e quase todas essas codificações contêm símbolos não ASCII e, simultaneamente, não são Unicode. E o Unicode também possui muitas codificações diferentes (como UTF-8, UTF-32 etc.).
Jpmc26
7

Pelos comentários, concordo que "ASCII estendido" é um termo muito ruim que realmente significa uma página de código que mapeia caracteres / pontos de código no intervalo 128-255, além do intervalo de pontos de código padrão 0-127 definido pelo ASCII.

O SQL Server oferece suporte a várias páginas de código por meio de agrupamentos. Caracteres não ASCII podem ser armazenados no varchar, desde que o agrupamento subjacente suporte o caractere.

O caractere '™' pode ser armazenado em colunas varchar / char quando a página de código de intercalação do SQL Server for 1250 ou superior. A consulta abaixo listará estes:

SELECT COLLATIONPROPERTY(name, 'CodePage') AS code_page, name, description
FROM sys.fn_helpcollations()
WHERE COLLATIONPROPERTY(name, 'CodePage') >= 1250
ORDER BY name;

Mas apenas um subconjunto desses também suporta o caractere '©', portanto, o agrupamento da coluna precisará ser um dos seguintes para oferecer suporte a ambos:

SELECT COLLATIONPROPERTY(name, 'CodePage') AS code_page, name, description
FROM sys.fn_helpcollations()
WHERE COLLATIONPROPERTY(name, 'CodePage') IN(
    1250
    ,1251
    ,1252
    ,1253
    ,1254
    ,1255
    ,1256
    ,1257
    ,1258
)
ORDER BY name;
Dan Guzman
fonte
4

Mas a definição de varchar diz que permite dados de string não unicode . Mas os símbolos de marca comercial (™) e registrada (®) são caracteres Unicode . A definição contradiz a propriedade do tipo de dados varchar?

Embora as outras respostas não estejam incorretas, acho que ajudaria a apontar uma confusão na terminologia básica. Eu enfatizei duas palavras na citação acima da pergunta como um exemplo dessa confusão. Quando a documentação do SQL Server fala de Unicode e não-Unicode de dados , eles são não falando sobre os personagens . Eles estão falando das seqüências de bytes que representam certos caracteres. A principal diferença entre os tipos de Unicode ( NCHAR, NVARCHAR,XML , e a obsoleta / mal NTEXT) e os tipos não-Unicode ( CHAR, VARCHARe a obsoleta / mal TEXT) é o que tipos de sequências de bytes que podem armazenar.

Os tipos não Unicode armazenam uma das várias codificações de 8 bits, enquanto os tipos Unicode armazenam uma única codificação Unicode de 16 bits: UTF-16 Little Endian. Como as outras respostas mencionaram, quais caracteres podem ser armazenados em uma codificação de 8 bits / não-Unicode depende da página de código, que é determinada pelo agrupamento. Enquanto outros observaram que o valor de byte de um "caractere" pode variar entre as páginas de código em que ele se encontra, o valor de byte pode até variar dentro da mesma página de código ao lidar com uma das várias páginas de código EBCDIC (variações do Windows- 1252), que são encontrados apenas nos mais antigos, não devem realmente ser usados ​​Collations do SQL Server (ou seja, aqueles com nomes começando comSQL_ ).

Portanto, a definição é precisa: todos os caracteres que você pode gerenciar para armazenar em um tipo não Unicode são sempre de 8 bits (mesmo que usem dois valores de 8 bits em combinação como um "caractere" único, que é o que o Double- As páginas de código do Byte Character Set / DBCS permitem). E os tipos de dados Unicode são sempre de 16 bits, mesmo que às vezes usem dois valores de 16 bits em combinação como um "caractere" único (ou seja, um par substituto que, por sua vez, representa um Caractere Suplementar).

E, devido ao suporte nativo do SQL Server à codificação UTF-8 para VARCHAR e CHARtipos de dados a partir do SQL Server 2019,

VARCHARnão pode mais ser chamado de "não Unicode". Portanto, começando com a primeira versão beta pública do SQL Server 2019 em setembro de 2018, devemos nos referir VARCHARcomo um "tipo de dados de 8 bits", mesmo quando falamos em termos de versões anteriores ao SQL Server 2019. Essa terminologia é válida para todos os quatro tipos de codificações que podem ser usadas comVARCHAR :

  1. ASCII estendido
  2. Conjuntos de caracteres de byte duplo (DBCS)
  3. EBCDIC
  4. UTF-8 (Unicode)

Apenas o TEXT tipo de dados (descontinuado no SQL Server 2005, portanto, não o use) é "não Unicode", mas isso é apenas um detalhe técnico, e a referência a ele como "tipo de dados de 8 bits" é precisa.

NVARCHAR,, NCHARe NTEXTpode ser referido como "UTF-16" ou "tipo de dados de 16 bits". A Oracle, acredito, usa a terminologia de "somente Unicode" paraNVARCHAR , mas isso não descarta claramente a possibilidade de usar UTF-8 (também uma codificação Unicode), que não funcionará, portanto, provavelmente é melhor as duas primeiras opções.

Para detalhes sobre as novas codificações UTF-8, consulte o meu post:

Suporte nativo a UTF-8 no SQL Server 2019: Salvador ou falso profeta?

PS: Estou trabalhando lentamente na atualização da documentação do SQL Server para refletir essas alterações.

PPS A Microsoft já atualizou algumas páginas com informações UTF-8, incluindo a documentação char e varchar mencionada na pergunta. Ele não contém mais a frase "não Unicode". Mas isso é apenas um FYI; isso não muda a questão, pois trata-se de codificações não Unicode contendo caracteres que, por engano, foram pensados ​​apenas como Unicode.

Solomon Rutzky
fonte
3

A pergunta contém um equívoco central sobre o que é Unicode. O conjunto de caracteres Unicode, juntamente com suas codificações, como UTF-8 e UTF-16, é uma das muitas maneiras de representar texto em um computador, e cujo objetivo é substituir todos os outros conjuntos de caracteres e codificações. Se "dados não Unicode" significasse "caracteres não presentes no Unicode", nenhum texto usado nesta resposta poderia ser armazenado nesse tipo, porque todas as letras do alfabeto latino e pontuação comum usada no inglês comum são incluído no Unicode.

As representações de texto podem ser amplamente pensadas em duas partes: um conjunto de caracteres mapeando os diferentes caracteres (letras, dígitos, símbolos, etc.) para números em um gráfico de referência; e uma codificação representando esses números como padrões de bits (no disco, em uma conexão de rede, etc.). Aqui estamos preocupados principalmente com a primeira parte: quais caracteres estão listados nos gráficos para um conjunto de caracteres específico.

Como o Unicode pretende ter números (chamados de "pontos de código") para todos os caracteres do mundo, referências como a Wikipedia geralmente se referem à posição Unicode de um caractere como uma peça padrão de informação de referência. No entanto, isso não significa que outros conjuntos de caracteres também não tenham um mapeamento para o mesmo caractere.

Um dos conjuntos de caracteres mais antigos e mais simples (e codificações) ainda em uso é o ASCII, que possui mapeamentos para 128 caracteres diferentes (0 a 127), porque usa 7 bits para codificar cada caractere. Como isso exclui muitos caracteres acentuados e símbolos comuns, as codificações posteriores usam 8 bits e mapeiam os mesmos 128 caracteres, adicionando ao conjunto de caracteres preenchendo as posições 128 a 255. Destacam- se os padrões ISO 8859-1 e ISO 8859- 15 e o código do Windows específico da Microsoft, página 1252 .

Então, para voltar para o MS SQL Server: a "seqüência de caracteres Unicode", como armazenados em um nchar, nvarcharou ntextcoluna, pode representar todos os caracteres mapeados no conjunto de caracteres Unicode, porque ele usa uma codificação Unicode para armazenar os dados. Uma "cadeia não-Unicode", como armazenado em um char, varcharou textcoluna, podem representar apenas os caracteres mapeados em alguma outra codificação . Tudo o que você pode armazenar em uma coluna não Unicode também pode ser armazenado em uma coluna Unicode, mas não vice-versa.

Para saber exatamente quais caracteres você pode armazenar, é necessário conhecer o "agrupamento" em uso, que determina o que a Microsoft chama de "página de código", conforme explicado nesta página de referência da Microsoft . Provavelmente, no seu caso, você está usando a muito comum Página de Código 1252, que mencionei anteriormente.

Os caracteres que você mencionou existem no Unicode e no Code Page 1252:

  • Trademark (™) aparece em Unicode na posição 8482 e em CP1252 na posição 153
  • Registrado (®), por acaso, aparece no Unicode e no CP1252 na posição 174
IMSoP
fonte
3
“Unicode é uma das muitas maneiras de codificar texto para uso em um computador” - Isso não está correto. Unicode é apenas uma coleção de caracteres e símbolos, em que cada caractere tem seu próprio ponto de código exclusivo , que é apenas um número. O trabalho de uma codificação é, então, corresponder esses pontos de código a uma sequência de bytes. UTF-8 e UTF-16 são codificações, Unicode não é.
cutuque
@poke Enquanto falo mais adiante na resposta, estou usando "codificação" aqui para representar "o mapeamento de caracteres para posições em um gráfico" e "representações dessas posições como uma sequência de bits". Talvez haja um termo melhor para usar, mas não tenho certeza do que seria.
IMSoP 30/01
3
Bem, você não pode simplesmente usar "codificação" com sua própria definição. Desculpe estar aqui, mas você não pode fazer isso em uma resposta que começa com "a pergunta contém um equívoco central sobre o que é Unicode" .
cutuca
2
IMSoP (e @poke): eu concordo completamente com poke em relação ao excesso de uso de "codificação" para significar algo diferente de codificação, embora eu também seja solidário com o dilema do IMSoP. Minha preferência é me referir ao Unicode como um conjunto de caracteres que possui várias codificações, enquanto que normalmente o conjunto e a codificação são usados ​​de forma intercambiável devido ao fato de ser um relacionamento 1 para 1 na maioria (ou talvez todo?) Do tempo.
Solomon Rutzky