web-dev-qa-db-fra.com

Quand `nvarchar / nchar` va être utilisé avec SQL Server 2019?

Avec SQL Server 2019, Microsoft introduit prise en charge UTF-8 pour les types de données CHAR et VARCHAR et dit:

Cette fonction peut permettre des économies de stockage importantes, selon le jeu de caractères utilisé. Par exemple, la modification d'un type de données de colonne existant avec des chaînes ASCII de NCHAR (10) à CHAR (10) à l'aide d'un classement activé UTF-8, se traduit par une réduction de près de 50% des exigences de stockage. la réduction est due au fait que NCHAR (10) nécessite 22 octets pour le stockage, tandis que CHAR (10) nécessite 12 octets pour la même chaîne Unicode.

UTF-8 semble support chaque script, donc en gros nous pouvons commencer à stocker les données Unicode dans les colonnes varchar et char. Et comme cela est dit dans la documentation, cela peut réduire la taille des tables et des index, et à partir de là, nous pouvons obtenir des performances encore meilleures, car une plus petite quantité de données est lue.

Je me demande si cela signifie que nous pouvons arrêter d'utiliser les colonnes nvarchar et nchar qui implémentent UTF-16?

Quelqu'un peut-il pointer un scénario et une raison, pour ne pas utiliser les types de données char avec le codage UTF et continuer à utiliser ceux n-chars?

11
gotqn

ceci peut réduire la taille des tables et des index (emphase ajoutée)

La réduction de la taille n'est possible que si la plupart des caractères sont essentiellement [space], 0 - 9, A - Z, a - z, et une ponctuation de base. En dehors de cet ensemble spécifique de caractères (en termes d'utilisation pratique, standard ASCII valeurs 32 - 126), vous serez au mieux de taille égale à NVARCHAR/UTF-16, ou dans de nombreux cas plus gros.

Je prévois de migrer les données car je pense que la lecture de moins de données entraînera de meilleures performances pour le système.

Faites attention. L'UTF-8 n'est pas un commutateur magique "tout réparer". Toutes choses étant égales par ailleurs, oui, lire moins améliore les performances. Mais ici "toutes les autres choses" sont pas égales. Même en stockant seulement standard ASCII caractères (ce qui signifie: tous les caractères font 1 octet, ce qui nécessite la moitié de l'espace par rapport au stockage dans NVARCHAR ), il y a une légère pénalité de performance pour l'utilisation de l'UTF-8. Je pense que le problème est dû au fait que l'UTF-8 est un codage de longueur variable, ce qui signifie que chaque octet doit être interprété tel qu'il est lu afin de savoir un caractère complet ou si l'octet suivant en fait partie. Cela signifie que toutes les opérations de chaîne doivent commencer au début et se poursuivre octet par octet. En revanche, NVARCHAR/UTF-16 est toujours 2 octets (même les caractères supplémentaires sont composés de deux points de code de 2 octets), de sorte que tout peut être lu par blocs de 2 octets.

Dans mes tests, même avec seulement standard ASCII caractères, le stockage des données au format UTF-8 n'a fourni aucune économie de temps écoulé, mais était nettement pire pour le temps CPU . Et cela sans la compression de données, donc au moins il y avait moins d'espace disque utilisé. Mais, lors de l'utilisation de la compression, l'espace requis pour UTF-8 n'était que de 1% à 1,5% plus petit. UTF-8.

Les choses deviennent plus compliquées lors de l'utilisation de NVARCHAR(MAX) car la compression Unicode ne fonctionne pas avec ce type de données, même si la valeur est suffisamment petite pour être stockée en ligne. Mais, si les données sont suffisamment petites, elles devraient toujours bénéficier de la compression de lignes ou de pages (auquel cas elles deviennent en fait plus rapides que UTF-8). Cependant, les données hors ligne ne peuvent utiliser aucune compression. Néanmoins, faire de la table un index de colonnes en cluster réduit considérablement la taille de NVARCHAR(MAX) (même si elle est encore légèrement plus grande que UTF-8 lors de l'utilisation de l'index de colonnes en cluster).

Quelqu'un peut-il pointer un scénario et une raison, ne pas utiliser les types de données char avec l'encodage UTF

Absolument. En fait, je ne trouve pas vraiment de raison impérieuse de l'utiliser dans la plupart des cas. Le seul scénario qui bénéficie vraiment de l'UTF-8 est:

  1. Les données sont surtout standard ASCII (valeurs 0 - 127)
  2. Il doit être Unicode car il peut-être doit stocker une plage de caractères plus large que celle disponible sur n'importe quelle page de code 8 bits (c'est-à-dire VARCHAR)
  3. La plupart des données sont stockées hors ligne (donc la compression de page ne fonctionne même pas)
  4. Vous disposez de suffisamment de données dont vous avez besoin/souhaitez réduire la taille pour des raisons autres que les performances de requête (par exemple, réduire la taille de la sauvegarde, réduire le temps requis pour la sauvegarde/restauration, etc.)
  5. Vous ne pouvez pas utiliser Clustered Columnstore Index (peut-être que l'utilisation de la table aggrave les performances dans ce cas?)

Mes tests montrent que dans presque tous les cas, NVARCHAR était plus rapide, surtout quand il y avait plus de données. En fait, 21k lignes avec une moyenne de 5k caractères par ligne exigeait 165 Mo pour UTF-8 et 236 Mo pour NVARCHAR non compressé. Et pourtant, le NVARCHAR était 2x plus rapide en temps écoulé, et au moins 2x plus rapide (parfois plus) en temps CPU. Pourtant, il occupait 71 Mo de plus sur le disque.

En dehors de cela, je ne recommanderais toujours pas d'utiliser UTF-8, au moins à partir de CTP 2, en raison d'une variété de bogues que j'ai trouvés dans cette fonctionnalité.

Pour une analyse détaillée de cette nouvelle fonctionnalité, y compris une explication des différences entre UTF-16 et UTF-8, et une liste de ces bogues, veuillez consulter mon article:

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

6
Solomon Rutzky

La prise en charge UTF-8 vous offre un nouvel ensemble d'options. Les économies d'espace potentielles (sans compression de ligne ou de page ) sont une considération, mais le choix du type et du codage devrait probablement être principalement fait sur la base des exigences réelles de comparaison, de tri, importation et exportation de données .

Vous devrez peut-être changer plus que vous ne le pensez, car par exemple un type nchar(1) fournit deux octets de stockage. Cela suffit pour stocker n'importe quel caractère dans BMP (points de code 000000 à 00FFFF). Certains des caractères de cette plage seraient codés avec seulement 1 octet en UTF-8 tandis que d'autres nécessiteraient 2 ou même 3 octets (voir ce tableau de comparaison pour plus de détails). Par conséquent, garantir la couverture du même ensemble de caractères en UTF-8 nécessiterait char(3).

Par exemple:

DECLARE @T AS table 
(
    n integer PRIMARY KEY,
    UTF16 nchar(1) COLLATE Latin1_General_CI_AS,
    UTF8 char(1) COLLATE Latin1_General_100_CI_AS_SC_UTF8
);

INSERT @T (n, UTF16, UTF8)
SELECT 911, NCHAR(911), NCHAR(911);

donne l'erreur familière:

Msg 8152, niveau 16, état 30, ligne xxx
Chaîne ou des données binaires seront tronquées.

Ou si l'indicateur de trace 460 est actif:

Msg 2628, niveau 16, état 1, ligne xxx
Les données de chaîne ou binaires seraient tronquées dans le tableau '@T', colonne 'UTF8'. Valeur tronquée: ''.

Le développement de la colonne UTF8 en char(2) ou varchar(2) résout l'erreur pour NCHAR(911):

DECLARE @T AS table 
(
    n integer PRIMARY KEY,
    UTF16 nchar(1) COLLATE Latin1_General_CI_AS,
    UTF8 varchar(2) COLLATE Latin1_General_100_CI_AS_SC_UTF8
);

INSERT @T (n, UTF16, UTF8)
SELECT 911, NCHAR(911), NCHAR(911);

Cependant, si c'était par exemple NCHAR(8364), vous devez développer davantage la colonne, en char(3) ou varchar(3).

Notez également que les classements UTF-8 utilisent tous des caractères supplémentaires, donc ne fonctionnera pas avec réplication.

Mis à part toute autre chose, la prise en charge UTF-8 est uniquement en aperçu pour le moment, donc non disponible pour une utilisation en production.

12
Paul White 9