web-dev-qa-db-fra.com

Pourquoi le type de données varchar autorise-t-il les valeurs unicode?

J'ai une table avec une colonne varchar. Il autorise les marques déposées (™), les droits d'auteur (©) et d'autres caractères Unicode comme indiqué ci-dessous.

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

Mais la définition de varchar dit, elle autorise les données de chaîne non unicode. Mais les symboles Trademark (™) et Registered (®) sont nicode caractères. La définition contredit-elle la propriété du type de données varchar? J'ai lu quelques liens comme le premier et le deuxième . Mais je ne pouvais toujours pas comprendre pourquoi il autorise la chaîne unicode lorsque la définition dit qu'il n'autorise que les valeurs de chaîne non unicode.

17
Shiva

Mais les symboles Trademark (™) et Registered (®) sont des caractères Unicode.

Vous avez tort ici. Vos chaînes ne contiennent que ascii caractères.

Voici un test simple qui vous montre que vos personnages sont tous ascii (+ certains extended ascii Avec des codes ascii entre 128 et 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;

Ici, vous pouvez clairement voir que tous vos caractères sont codés sur 1 octet:

enter image description here

Oui, ce ne sont pas des caractères ascii purs mais ils sont ASCII étend .

Ici, je vous montre le vrai caractère unicode Trademark(™) et son code et sa représentation binaire:

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;

enter image description here

Enfin, vous pouvez voir que le caractère unicode Trademark(™) a le code 8482 et non 153:

select nchar(8482), nchar(153)
15
sepupic

D'après les commentaires, je conviens que "ASCII étendu" est un terme vraiment mauvais qui signifie en fait une page de codes qui mappe les caractères/points de code dans la plage 128-255, au-delà de la plage de points de code 0-127 standard définie par ASCII.

SQL Server prend en charge de nombreuses pages de codes via des classements. Les caractères non ASCII peuvent être stockés dans varchar tant que le classement sous-jacent prend en charge le caractère.

Le caractère "™" peut être stocké dans des colonnes varchar/char lorsque la page de codes de classement SQL Server est supérieure ou égale à 1250. La requête ci-dessous les énumérera:

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

Mais seul un sous-ensemble de ceux-ci prend également en charge le caractère "©", de sorte que le classement des colonnes devra être l'un des suivants pour prendre en charge les deux:

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;
7
Dan Guzman

Mais la définition de varchar dit, il autorise les données de chaîne non-unicode données . Mais les symboles Trademark (™) et Registered (®) sont des caractères Unicode . La définition contredit-elle la propriété du type de données varchar?

Bien que les autres réponses ne soient pas incorrectes, je pense qu'il serait utile de signaler une confusion dans la terminologie de base. J'ai souligné deux mots dans la citation ci-dessus de la question comme exemple de cette confusion. Lorsque la documentation SQL Server parle de données Unicode et non Unicode , ce sont pas qui parlent de caractères . Ils parlent des séquences d'octets qui représentent certains caractères. La principale différence entre les types Unicode (NCHAR, NVARCHAR, XML et les obsolètes/evil NTEXT) et les types non Unicode (CHAR, VARCHAR, et le déprécié/mauvais TEXT) est ce types de séquences d'octets qu'ils peuvent stocker.

Les types non Unicode stockent l'un des codages 8 bits, tandis que les types Unicode stockent un seul codage Unicode 16 bits: UTF-16 Little Endian. Comme les autres réponses l'ont mentionné, quels caractères peuvent être stockés dans un codage 8 bits/non Unicode dépend de la page de codes, qui est déterminée par le classement. Alors que d'autres ont noté que la valeur en octets d'un "caractère" peut varier selon les pages de codes sur lesquelles il se trouve, la valeur en octets peut même varier au sein de la même page de codes lorsqu'il s'agit de l'une des plusieurs pages de codes EBCDIC (variations de Windows 1252), qui ne se trouvent que dans les anciennes versions de SQL Server, ne devraient pas vraiment être utilisées (c'est-à-dire celles dont le nom commence par SQL_).

Par conséquent, la définition est précise: tous les caractères que vous pouvez gérer pour stocker dans un type non Unicode sont toujours 8 bits (même s'ils utilisent deux valeurs 8 bits en combinaison comme un seul "caractère", ce qui est ce que le Double- Le jeu de caractères octet/les pages de codes DBCS le permettent). Et les types de données Unicode sont toujours 16 bits, même s'ils utilisent parfois deux valeurs 16 bits en combinaison comme un seul "caractère" (c'est-à-dire une paire de substitution qui à son tour représente un caractère supplémentaire).

ET, en raison de la prise en charge native de SQL Server pour le codage UTF-8 pour les types de données VARCHAR et CHAR à partir de SQL Server 2019,

VARCHAR ne peut plus être appelé "non-Unicode". Ainsi, à partir de la première version bêta publique de SQL Server 2019 en septembre 2018, nous devrions désigner VARCHAR comme un "type de données 8 bits", même lorsque nous parlons en termes de versions antérieures à SQL Server 2019. Cette terminologie vaut pour les 4 types d'encodages qui peuvent être utilisés avec VARCHAR:

  1. ASCII étendu
  2. Jeux de caractères codés sur deux octets (DBCS)
  3. EBCDIC
  4. UTF-8 (Unicode)

Seul le type de données TEXT (obsolète à partir de SQL Server 2005, donc ne l'utilisez pas) est "non-Unicode", mais ce n'est qu'une technicité, et le qualifier de "type de données 8 bits" est précis.

NVARCHAR, NCHAR et NTEXT peuvent être appelés "UTF-16" ou "type de données 16 bits". Oracle, je crois, utilise la terminologie de "Unicode uniquement" pour NVARCHAR, mais cela n'exclut pas clairement la possibilité d'utiliser UTF-8 (également un encodage Unicode), qui ne fonctionnera pas, il est donc préférable de s'en tenir aux deux premières options.

Pour plus de détails sur les nouveaux encodages UTF-8, veuillez consulter mon article:

Prise en charge native UTF-8 dans SQL Server 2019: Sauveur ou faux prophète?

P.S. Je progresse lentement dans la mise à jour de la documentation de SQL Server pour refléter ces changements.

P.P.S. Microsoft a déjà mis à jour certaines pages avec des informations UTF-8, y compris la documentation char et varchar référencée dans la question. Il ne contient plus l'expression "non-Unicode". Mais ce n'est qu'un FYI; cela ne change pas la question car il s'agit d'encodages non Unicode contenant des caractères qui ont été pensés à tort comme Unicode uniquement.

4
Solomon Rutzky

La question contient une idée fausse centrale sur ce qu'est Unicode. Le jeu de caractères Unicode, ainsi que ses encodages tels que UTF-8 et UTF-16, est l'une des nombreuses façons de représenter le texte dans un ordinateur, et dont le but est de remplacer tous les autres jeux de caractères et encodages. Si "données non Unicode" signifiait "caractères non présents dans Unicode", alors aucun du texte que j'ai utilisé dans cette réponse ne pourrait être stocké dans ce type, car toutes les lettres de l'alphabet latin et la ponctuation courante utilisées dans l'anglais courant sont inclus dans Unicode.

Les représentations textuelles peuvent être globalement envisagées en deux parties: un jeu de caractères mappant les différents caractères (lettres, chiffres, symboles, etc.) aux nombres sur une référence graphique; et un codage représentant ces nombres sous forme de modèles de bits (sur disque, via une connexion réseau, etc.). Ici, nous nous intéressons principalement à la première partie: quels caractères sont répertoriés dans les graphiques pour un jeu de caractères particulier.

Étant donné qu'Unicode vise à avoir des nombres (qu'il appelle des "points de code") pour chaque caractère dans le monde, des références comme Wikipedia se réfèrent souvent à la position Unicode d'un caractère comme une information standard de référence. Cependant, cela ne signifie pas que les autres jeux de caractères n'ont pas de mappage pour ce même caractère.

L'un des jeux de caractères (et codages) les plus anciens et les plus simples encore en usage est ASCII, qui a des mappages pour 128 caractères différents (0 à 127), car il utilise 7 bits pour coder chaque caractère. Étant donné que cela exclut de nombreux caractères accentués et symboles communs, les encodages ultérieurs utilisent 8 bits et mappent les mêmes 128 premiers caractères, ajoutant au jeu de caractères en remplissant les positions 128 à 255. Parmi ceux-ci figurent la norme ISO 8859-1 et ISO 8859-15 , et le spécifique à Microsoft Page de code Windows 1252 .

Donc, pour revenir à MS SQL Server: une "chaîne Unicode", telle qu'elle est stockée dans une colonne nchar, nvarchar ou ntext, peut représenter tous les caractères mappés dans le jeu de caractères Unicode, car il utilise un codage Unicode pour stocker les données. Une "chaîne non Unicode", telle qu'elle est stockée dans une colonne char, varchar ou text, ne peut représenter que les caractères mappés dans un autre encodage . Tout ce que vous pouvez stocker dans une colonne non Unicode peut également être stocké dans une colonne Unicode, mais pas l'inverse.

Pour savoir exactement quels caractères vous pouvez stocker, vous devez connaître le "classement" utilisé, qui dicte ce que Microsoft appelle une "page de code", comme expliqué sur cette page de référence Microsoft . Il est probable que dans votre cas, vous utilisez la page de code très courante 1252, que j'ai mentionnée plus tôt.

Les caractères que vous avez mentionnés existent à la fois dans Unicode et dans la page de code 1252:

  • Trademark (™) apparaît en Unicode à la position 8482 et en CP1252 à la position 153
  • Enregistré (®), en l'occurrence, apparaît à la fois dans Unicode et CP1252 à la position 174
3
IMSoP