Quelles sont les principales différences de performances entre les types de données varchar et nvarchar SQL Server ?

StackOverflow https://stackoverflow.com/questions/35366

Question

Je travaille sur une base de données pour une petite application Web dans mon école en utilisant SQL Server 2005.
Je vois quelques écoles de pensée sur la question de varchar contre nvarchar:

  1. Utiliser varchar sauf si vous traitez beaucoup de données internationalisées, utilisez nvarchar.
  2. Utilisez simplement nvarchar pour tout.

Je commence à voir les mérites du point de vue 2.Je sais que nvarchar prend deux fois plus d'espace, mais ce n'est pas nécessairement un gros problème puisque cela ne stockera les données que de quelques centaines d'étudiants.Pour moi, il semble qu'il serait plus simple de ne pas s'en soucier et de simplement permettre à tout d'utiliser nvarchar.Ou y a-t-il quelque chose qui me manque ?

Était-ce utile?

La solution

Utilisez toujours nvarchar.

Vous n'aurez peut-être jamais besoin des caractères à deux octets pour la plupart des applications.Cependant, si vous devez prendre en charge les langages à deux octets et que votre schéma de base de données ne prend en charge qu'un seul octet, il est très coûteux de revenir en arrière et de modifier l'ensemble de votre application.

Le coût de la migration d'une application de varchar vers nvarchar sera bien supérieur au peu d'espace disque supplémentaire que vous utiliserez dans la plupart des applications.

Autres conseils

L'espace disque n'est pas le problème...mais la mémoire et les performances le seront.Doublez les lectures de page, doublez la taille de l'index, LIKE étrange et = comportement constant, etc.

Avez-vous besoin de stocker des scripts chinois, etc. ?Oui ou non...

Et de MS BOL "Effets de stockage et de performances d'Unicode"

Modifier:

Question SO récente soulignant à quel point les performances de nvarchar peuvent être mauvaises...

SQL Server utilise un processeur élevé lors de la recherche dans les chaînes nvarchar

Être cohérent!REJOINDRE un VARCHAR à NVARCHAR a un gros impact sur les performances.

nvarchar va avoir une surcharge importante en termes de mémoire, de stockage, de jeu de travail et d'indexation, donc si les spécifications l'exigent, ce sera vraiment le cas. jamais être nécessaire, ne vous embêtez pas.

Je n'aurais pas de règle stricte "toujours nvarchar" car cela peut être un gaspillage complet dans de nombreuses situations - en particulier ETL à partir d'ASCII/EBCDIC ou d'identifiants et de colonnes de code qui sont souvent des clés et des clés étrangères.

D'un autre côté, il existe de nombreux cas de colonnes, dans lesquels je serais sûr de poser cette question tôt et si je n'obtenais pas immédiatement une réponse ferme et rapide, je créerais la colonne nvarchar.

Pour votre application, nvarchar convient car la taille de la base de données est petite.Dire "toujours utiliser nvarchar" est une simplification excessive.Si vous n'êtes pas obligé de stocker des éléments comme des Kanji ou d'autres caractères fous, utilisez VARCHAR, cela utilisera beaucoup moins d'espace.Mon prédécesseur dans mon poste actuel a conçu quelque chose en utilisant NVARCHAR alors que ce n'était pas nécessaire.Nous l'avons récemment basculé vers VARCHAR et économisé 15 Go uniquement sur cette table (elle a été hautement écrite).De plus, si vous avez ensuite un index sur cette table et que vous souhaitez inclure cette colonne ou créer un index composite, vous venez d'augmenter la taille de votre fichier d'index.

Soyez simplement réfléchi dans votre décision ;dans le développement SQL et les définitions de données, il semble rarement y avoir de "réponse par défaut" (à part éviter à tout prix les curseurs, bien sûr).

J'hésite à ajouter ici une autre réponse car il y en a déjà pas mal, mais quelques points doivent être soulignés qui n'ont pas été évoqués ou qui n'ont pas été clairement évoqués.

D'abord: Faire pas toujours utiliser NVARCHAR.Il s’agit d’une attitude/approche très dangereuse et souvent coûteuse.Et ce n'est pas mieux de dire "Jamais utiliser des curseurs" car ils constituent parfois le moyen le plus efficace de résoudre un problème particulier, et la solution de contournement courante consistant à effectuer un WHILE la boucle sera presque toujours plus lente qu'une correctement fait Curseur.

La seule fois où vous devriez utiliser le terme « toujours », c’est lorsque vous conseillez de « toujours faire ce qui est le mieux adapté à la situation ».Certes, cela est souvent difficile à déterminer, surtout lorsqu'on essaie d'équilibrer les gains à court terme en termes de temps de développement (responsable :"nous avons besoin de cette fonctionnalité -- dont vous ne connaissiez pas l'existence jusqu'à maintenant -- il y a une semaine !") avec des coûts de maintenance à long terme (le manager qui a initialement fait pression sur l'équipe pour qu'elle réalise un projet de 3 mois dans un sprint de 3 semaines :"pourquoi avons-nous ces problèmes de performances ?Comment aurions-nous pu faire X qui n'a aucune flexibilité ?Nous ne pouvons pas nous permettre un sprint ou deux pour résoudre ce problème.Que pouvons-nous faire en une semaine pour pouvoir revenir à nos priorités ?Et nous devons absolument consacrer plus de temps au design pour que cela ne continue pas !").

Deuxième: La réponse de @gbn aborde certains points très importants à prendre en compte lors de la prise de certaines décisions de modélisation de données lorsque le chemin n'est pas clair à 100 %.Mais il y a encore plus à considérer :

  • taille des fichiers journaux de transactions
  • temps nécessaire à la réplication (si vous utilisez la réplication)
  • temps nécessaire pour ETL (si ETLing)
  • temps nécessaire pour envoyer les journaux à un système distant et les restaurer (si vous utilisez Log Shipping)
  • taille des sauvegardes
  • le temps nécessaire pour terminer la sauvegarde
  • le temps nécessaire pour effectuer une restauration (cela pourrait être important un jour ;-)
  • taille nécessaire pour tempdb
  • performances des déclencheurs (pour les tables insérées et supprimées stockées dans tempdb)
  • performances du versioning des lignes (si vous utilisez SNAPSHOT ISOLATION, puisque le magasin de versions est dans tempdb)
  • possibilité d'obtenir un nouvel espace disque lorsque le directeur financier déclare qu'il vient de dépenser 1 million de dollars pour un SAN l'année dernière et qu'il n'autorisera donc pas 250 000 dollars supplémentaires pour du stockage supplémentaire
  • temps nécessaire pour effectuer les opérations INSERT et UPDATE
  • durée nécessaire à la maintenance de l'index
  • etc, etc.

Le gaspillage d’espace a un énorme effet de cascade sur l’ensemble du système.J'ai écrit un article détaillant explicitement ce sujet : Le disque est bon marché !ORLY ? (inscription gratuite requise;désolé, je ne contrôle pas cette politique).

Troisième: Bien que certaines réponses se concentrent à tort sur l'aspect « il s'agit d'une petite application », et que d'autres suggèrent à juste titre « d'utiliser ce qui est approprié », aucune des réponses n'a fourni de véritables conseils au PO.Un détail important mentionné dans la question est qu'il s'agit d'une page Web pour leur école.Super!Nous pouvons donc suggérer que :

  • Les champs pour les noms des étudiants et/ou des professeurs doivent probablement être NVARCHAR car, avec le temps, il est de plus en plus probable que des noms d’autres cultures apparaissent dans ces lieux.
  • Mais pour l’adresse et les noms de villes ?Le but de l'application n'a pas été indiqué (cela aurait été utile), mais en supposant que les enregistrements d'adresse, le cas échéant, se rapportent uniquement à une région géographique particulière (c'est-à-direune seule langue/culture), puis utilisez VARCHAR avec la page de codes appropriée (qui est déterminée à partir du classement du champ).
  • Si vous stockez des codes ISO d’État et/ou de pays (pas besoin de stocker INT / TINYINT puisque les codes ISO sont de longueur fixe, lisibles par l'homme et, bien, standard :) utilisez CHAR(2) pour les codes à deux lettres et CHAR(3) si vous utilisez des codes à 3 lettres.Et pensez à utiliser un classement binaire tel que Latin1_General_100_BIN2.
  • Si vous stockez des codes postaux (c.-à-d.codes postaux), utilisez VARCHAR puisqu'il s'agit d'une norme internationale de ne jamais utiliser de lettre en dehors de A-Z.Et oui, j'utilise toujours VARCHAR même si vous ne stockez que les codes postaux américains et non INT, car les codes postaux ne sont pas des chiffres, ce sont des chaînes, et certains d'entre eux ont un "0" en tête.Et pensez à utiliser un classement binaire tel que Latin1_General_100_BIN2.
  • Si vous stockez des adresses e-mail et/ou des URL, utilisez NVARCHAR puisque les deux peuvent désormais contenir des caractères Unicode.
  • et ainsi de suite....

Quatrième: Maintenant que tu as NVARCHAR les données occupent deux fois plus d'espace que nécessaire pour des données qui s'intègrent parfaitement dans VARCHAR ("s'adapte bien" = ne se transforme pas en "?") et d'une manière ou d'une autre, comme par magie, l'application s'est développée et il y a maintenant des millions d'enregistrements dans au moins un de ces domaines où la plupart les lignes sont en ASCII standard mais certaines contiennent des caractères Unicode, vous devez donc conserver NVARCHAR, considérer ce qui suit:

  1. Si vous utilisez SQL Server 2008-2016 RTM et sont sur Enterprise Edition, OU si vous utilisez SQL Server 2016 SP1 (qui a rendu la compression de données disponible dans toutes les éditions) ou une version plus récente, vous pouvez activer Compression des données.La compression de données peut (mais ne le fera pas « toujours ») compresser les données Unicode dans NCHAR et NVARCHAR des champs.Les facteurs déterminants sont :

    1. NCHAR(1 - 4000) et NVARCHAR(1 - 4000) Utilisez le Schéma de compression standard pour Unicode, mais uniquement à partir de SQL Server 2008 R2, ET uniquement pour les données IN ROW, pas OVERFLOW !Cela semble être meilleur que l'algorithme de compression ROW/PAGE classique.
    2. NVARCHAR(MAX) et XML (et je suppose aussi VARBINARY(MAX), TEXT, et NTEXT) les données qui sont IN ROW (et non hors ligne dans les pages LOB ou OVERFLOW) peuvent au moins être compressées en PAGE, mais pas LIGNE compressée.Bien entendu, la compression PAGE dépend de la taille de la valeur en ligne :J'ai testé avec VARCHAR(MAX) et j'ai vu que les lignes de 6 000 caractères/octets ne seraient pas compressées, mais que les lignes de 4 000 caractères/octets le faisaient.
    3. Toutes les données OFF ROW, LOB ou OVERLOW = Pas de compression pour vous !
  2. Si vous utilisez SQL Server 2005 ou 2008 - 2016 RTM et pas sur Enterprise Edition, vous pouvez avoir deux champs :un VARCHAR et une NVARCHAR.Par exemple, disons que vous stockez des URL qui sont pour la plupart composées uniquement de caractères ASCII de base (valeurs 0 à 127) et qui correspondent donc à VARCHAR, mais contiennent parfois des caractères Unicode.Votre schéma peut inclure les 3 champs suivants :

      ...
      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))
    );
    

    Dans ce modèle, vous seulement SÉLECTIONNEZ parmi [URL] colonne calculée.Pour l'insertion et la mise à jour, vous déterminez quel champ utiliser en voyant si la conversion modifie la valeur entrante, qui doit être de NVARCHAR taper:

    INSERT INTO TableName (..., URLa, URLu)
    VALUES (...,
            IIF (CONVERT(VARCHAR(2048), @URL) = @URL, @URL, NULL),
            IIF (CONVERT(VARCHAR(2048), @URL) <> @URL, NULL, @URL)
           );
    
  3. Vous pouvez GZIP les valeurs entrantes dans VARBINARY(MAX) puis décompressez en sortant :

    • Pour SQL Server 2005-2014 :vous pouvez utiliser SQLCLR. SQL# (une bibliothèque SQLCLR que j'ai écrite) est livrée avec Util_GZip et Util_GUnzip dans la version gratuite
    • Pour SQL Server 2016 et versions ultérieures :vous pouvez utiliser le module intégré COMPRESS et DECOMPRESS fonctions, qui sont également GZip.
  4. Si vous utilisez SQL Server 2017 ou une version plus récente, vous pouvez envisager de faire de la table un index Clustered Columnstore.

  5. Bien que ce ne soit pas encore une option viable, SQL Server 2019 introduit la prise en charge native d'UTF-8 dans VARCHAR / CHAR Types de données.Il y a actuellement trop de bugs pour qu'il puisse être utilisé, mais s'ils sont corrigés, alors c'est une option pour quelques scénarios.Veuillez consulter mon message : "Prise en charge native de l'UTF-8 dans SQL Server 2019 :Sauveur ou faux prophète ?", pour une analyse détaillée de cette nouvelle fonctionnalité.

Étant donné que votre application est petite, il n'y a pratiquement aucune augmentation de coût appréciable liée à l'utilisation de nvarchar par rapport à varchar, et vous vous épargnez des maux de tête potentiels plus tard si vous avez besoin de stocker des données Unicode.

En général;Commencez avec le type de données le plus cher et celui qui présente le moins de contraintes. Mettez-le en production.Si les performances commencent à poser problème, découvrez ce qui est réellement stocké dans ces nvarchar Colonnes.Y a-t-il des personnages là-dedans qui ne rentreraient pas dans varchar?Sinon, passez à varchar.N'essayez pas de pré-optimiser avant de savoir où se situe le problème.Je suppose que le choix entre nvarchar/varchar n'est pas ce qui va ralentir votre application dans un avenir proche.Il y aura d'autres parties de l'application où le réglage des performances vous apportera beaucoup plus bon rapport qualité prix.

Ces dernières années, tous nos projets ont utilisé NVARCHAR pour tout, puisque tous ces projets sont multilingues.Données importées de sources externes (par ex.un fichier ASCII, etc.) est converti en Unicode avant d'être inséré dans la base de données.

Je n'ai pas encore rencontré de problèmes liés aux performances des index plus importants, etc.Les index utilisent plus de mémoire, mais la mémoire est bon marché.

Que vous utilisiez des procédures stockées ou construisiez SQL à la volée, assurez-vous que toutes les constantes de chaîne portent le préfixe N (par ex.SET @foo = N'Hello world.';) donc la constante est également Unicode.Cela évite toute conversion de type de chaîne au moment de l'exécution.

YMMV.

Je peux parler d'expérience à ce sujet, méfiez-vous nvarchar.Sauf si vous en avez absolument besoin, ce type de champ de données détruit les performances sur une base de données plus grande.J'ai hérité d'une base de données qui souffrait en termes de performances et d'espace.Nous avons pu réduire la taille d’une base de données de 30 Go de 70 % !D'autres modifications ont été apportées pour améliorer les performances, mais je suis sûr que le varcharCela a également été d'une grande aide.Si votre base de données a le potentiel de développer des tables jusqu'à un million d'enregistrements, évitez nvarchar à tout prix.

Je suis souvent confronté à cette question au travail :

  • Flux FTP d'inventaire et de prix - Les descriptions d'articles et autres textes étaient en nvarchar lorsque varchar fonctionnait correctement.Leur conversion en varchar a réduit la taille du fichier presque de moitié et a vraiment aidé avec les téléchargements.

  • Le scénario ci-dessus a bien fonctionné jusqu'à ce que quelqu'un mette un caractère spécial dans la description de l'article (peut-être une marque, je ne m'en souviens plus)

Je n'utilise toujours pas nvarchar à chaque fois sur varchar.En cas de doute ou de potentiel de caractères spéciaux, j'utilise nvarchar.Je trouve que j'utilise varchar principalement lorsque je contrôle à 100% ce qui peuple le champ.

Pourquoi, dans toute cette discussion, n’a-t-il été fait aucune mention d’UTF-8 ?Être capable de stocker l'ensemble des caractères Unicode ne signifie pas qu'il faut toujours allouer deux octets par caractère (ou "point de code" pour utiliser le terme UNICODE).Tout l’ASCII est en UTF-8.SQL Server vérifie-t-il pour les champs VARCHAR() que le texte est en ASCII strict (c'est-à-direoctet supérieur, bit zéro) ?J'espère que non.

Si alors vous souhaitez stocker l'Unicode et Si vous souhaitez une compatibilité avec les anciennes applications ASCII uniquement, je pense que l'utilisation de VARCHAR() et UTF-8 serait la solution miracle :Il n’utilise plus d’espace que lorsque cela est nécessaire.

Pour ceux d'entre vous qui ne connaissent pas UTF-8, puis-je recommander une amorce.

Il y aura des cas exceptionnels où vous souhaiterez délibérément restreindre le type de données pour le garantir n'a pas contiennent des caractères d'un certain ensemble.Par exemple, j'ai eu un scénario dans lequel je devais stocker le nom de domaine dans une base de données.L'internationalisation des noms de domaine n'était pas fiable à l'époque, il était donc préférable de restreindre la saisie au niveau de base et d'éviter tout problème potentiel.

Si vous utilisez NVARCHAR simplement parce qu'une procédure stockée système l'exige, l'occurrence la plus fréquente étant inexplicablement sp_executesql, et votre SQL dynamique est très long, il serait préférable, du point de vue des performances, de faire toutes les manipulations de chaînes (concaténation, remplacement, etc.) dans VARCHAR puis convertir le résultat final en NVARCHAR et l'introduire dans le paramètre proc.Alors non, n'utilisez pas toujours NVARCHAR!

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top