¿Cuáles son las principales diferencias de rendimiento entre los tipos de datos varchar y nvarchar SQL Server?

236

Estoy trabajando en una base de datos para una pequeña aplicación web en mi escuela usando SQL Server 2005.
Veo un par de escuelas de pensamiento sobre el tema de varcharvs nvarchar:

  1. Use a varcharmenos que maneje una gran cantidad de datos internacionalizados, luego usenvarchar .
  2. Solo úsalo nvarcharpara todo.

Estoy empezando a ver los méritos de la vista 2. Sé que nvarchar ocupa el doble de espacio, pero eso no es necesariamente un gran problema ya que esto solo va a almacenar datos para unos pocos cientos de estudiantes. Para mí, parece que sería más fácil no preocuparse por eso y simplemente permitir que todo use nvarchar. ¿O hay algo que me falta?

Jason Baker
fuente
pregunta similar aquí: stackoverflow.com/questions/312170/… EDIT por le dorfier: que curiosamente llegó exactamente a la conclusión opuesta.
Booji Boy
66
referencia hilo mucho más extenso que llegó a la conclusión opuesta. stackoverflow.com/questions/312170/…
dkretz
2
Jason: Espero que esta no sea una solicitud inapropiada, pero ¿podrías considerar cambiar la respuesta aceptada a gbn ? La respuesta de JoeBarone es horriblemente incorrecta por muchas razones. Tenerlo "aceptado" engaña a los novatos para que tomen malas decisiones. Es innecesario y derrochador "usar siempre NVARCHAR", y puede tener impactos muy negativos en el rendimiento y los costos / presupuestos de hardware. Unas pocas filas, incluso unos pocos miles, no importan. Pero los sistemas crecen más rápidamente de lo que la gente espera, por lo que la respuesta actual aceptada es un perjuicio para la comunidad. Gracias.
Solomon Rutzky

Respuestas:

140

Siempre use nvarchar.

Es posible que nunca necesite los caracteres de doble byte para la mayoría de las aplicaciones. Sin embargo, si necesita admitir idiomas de doble byte y solo tiene compatibilidad de un solo byte en el esquema de su base de datos, es realmente costoso volver atrás y modificar en toda su aplicación.

El costo de migrar una aplicación de varchar a nvarchar será mucho mayor que el poco espacio en disco extra que usará en la mayoría de las aplicaciones.

Joe Barone
fuente
44
es mucho más difícil regresar y agregar soporte para mensajes de texto multilingües, zonas horarias, unidades de medida y moneda, por lo que todos DEBEN codificarlos siempre en su aplicación desde el primer día, SIEMPRE (incluso si solo está en la página web de su página de inicio) aplicación)!
KM.
82
¿Qué pasa con el tamaño del índice, el uso de memoria, etc.? ¿Supongo que siempre usas int cuando podrías usar tinyint también "por si acaso"?
gbn
99
Siempre codificar / planificar un sitio multilingüe (cuando no tienes idea de que lo necesitarás) es como decirle a todos los adultos jóvenes que deben comprar un gran SUV de 8 asientos y gas para su primer auto ... después de todo , podrían casarse algún día y tener 6 hijos,. Prefiero disfrutar del rendimiento y la eficiencia mientras pueda y pagar el precio de la actualización cuando la necesite.
EJ Brennan
44
@cbmeeks: Yo no hago código por lo que no sé. Pero si se puede utilizar sin ningún impacto en el rendimiento notable, a continuación, sus bases de datos no son lo suficientemente grandes para que importa ...
gbn
6060
Por lo general, cuando las personas comienzan su respuesta con la palabra "Siempre", entonces debe ignorar todo lo que viene después de eso. (Observe que comencé esa declaración con la palabra "generalmente" :)
Brandon Moore
226

El espacio en disco no es el problema ... pero la memoria y el rendimiento sí lo serán. Duplica las lecturas de la página, duplica el tamaño del índice, LIKE extraño y = comportamiento constante, etc.

¿Necesita almacenar el script chino, etc.? Si o no...

Y de MS BOL " Almacenamiento y efectos de rendimiento de Unicode "

Editar :

Pregunta SO reciente que destaca cuán malo puede ser el rendimiento de nvarchar ...

SQL Server usa CPU alta cuando busca dentro de cadenas nvarchar

gbn
fuente
19
+1, si su aplicación se internacionaliza, tendrá muchos otros problemas de los que preocuparse: una búsqueda / reemplazo de nvarchar: mensajes de texto multilingües, zonas horarias, unidades de medida y moneda
KM.
2
Pero, ¿qué pasa si necesita almacenar un nombre extranjero a veces, como José o Bjørn?
Qwertie
77
@Qwertie: entonces usas nvarchar. Lo que no haces lo usas innecesariamente. Esos 2 nombres encajan en varchar de todos modos IIRC
gbn
66
Decir que el espacio en disco no es un problema no es cierto para todos. Hemos usado ingenuamente nvarchar innecesariamente en una gran aplicación bancaria con miles de millones de registros almacenados durante muchos años. Con el costoso almacenamiento basado en SAN con replicación, respaldo y recuperación ante desastres, esto puede traducirse en millones de dólares en costos para nvarchar vs varchar. Sin mencionar que hay un gran impacto en el rendimiento (100%) al tener que leer el doble de bytes del disco por cada lectura.
codemonkey
2
@codemonkey, et al: Hice lo que pude para abordar el problema del espacio desperdiciado de manera integral en el siguiente artículo: ¡El disco es barato! ORLY? (sin embargo, se requiere registro gratuito). El objetivo del artículo es ayudar a prevenir la situación en la que se encontró codemonkey con respecto al almacenamiento costoso a nivel empresarial.
Solomon Rutzky
59

¡Se consistente! Unirse a un VARCHAR en NVARCHAR tiene un gran éxito en el rendimiento.

Thomas Harlan
fuente
115
Si está haciendo uniones en los campos de caracteres, entonces su base de datos probablemente tenga problemas peores que si usa nvarchar o varchar, en general.
Brandon Moore
@Thomas Harlan Una simple prueba me demuestra que no hay una diferencia tangible entre unirse nvarchara varcharvs convertir nvarchara varchary unirse a varchar. A menos, por supuesto, que quisieras ser coherente en los tipos de datos de la columna, no en la unión.
ajeh
1
@ajeh y Thomas: 1) las pruebas "simples" a menudo son engañosas ya que no cubren variaciones que causan diferencias en el comportamiento. 2) Si uno ve un impacto drástico en el rendimiento al mezclar VARCHARy NVARCHAR, eso debería deberse a la indexación de la VARCHARcolumna junto con el tipo de Colación utilizada para esa columna (y, por lo tanto, el índice). Cubro este tema en detalle en la siguiente publicación de blog: Impacto en los índices al mezclar tipos VARCHAR y NVARCHAR .
Solomon Rutzky
44

nvarchar tendrá una sobrecarga significativa en la memoria, el almacenamiento, el conjunto de trabajo y la indexación, por lo que si las especificaciones dictan que realmente nunca será necesario, no se moleste.

No tendría una regla dura y rápida de "siempre nvarchar" porque puede ser un desperdicio completo en muchas situaciones, particularmente ETL de ASCII / EBCDIC o identificadores y columnas de código que a menudo son claves y claves foráneas.

Por otro lado, hay muchos casos de columnas, donde me aseguraría de hacer esta pregunta temprano y si no obtuviera una respuesta rápida y dura de inmediato, haría que la columna sea nvarchar.

Cade Roux
fuente
26

Dudo en agregar otra respuesta aquí ya que ya hay bastantes, pero es necesario hacer algunos puntos que no se han hecho o no se han hecho con claridad.

Primero: Do no siempre se utilizan NVARCHAR. Esa es una actitud / enfoque muy peligroso, y a menudo costoso. Y no es mejor decir " Nunca use cursores" ya que a veces son los medios más eficientes para resolver un problema en particular, y la solución común de hacer un WHILEbucle casi siempre será más lenta que una correcta. Cursor hecho .

La única vez que debe usar el término "siempre" es cuando aconseja "hacer siempre lo mejor para la situación". Es cierto que a menudo es difícil de determinar, especialmente cuando se trata de equilibrar las ganancias a corto plazo en el tiempo de desarrollo (gerente: "necesitamos esta característica, que no conocías hasta ahora, ¡hace una semana!") costos de mantenimiento a largo plazo (gerente que inicialmente presionó al equipo para completar un proyecto de 3 meses en un sprint de 3 semanas: "¿por qué estamos teniendo estos problemas de rendimiento? ¿Cómo podríamos haber hecho X que no tiene flexibilidad? No podemos pagar un sprint o dos para arreglar esto. ¿Qué podemos hacer en una semana para volver a nuestros elementos prioritarios? ¡Y definitivamente necesitamos pasar más tiempo en el diseño para que esto no siga sucediendo! ").

Segundo: la respuesta de @ gbn toca algunos puntos muy importantes a considerar al tomar ciertas decisiones de modelado de datos cuando la ruta no es 100% clara. Pero aún hay más para considerar:

  • tamaño de los archivos de registro de transacciones
  • tiempo que lleva replicarse (si usa la replicación)
  • tiempo que tarda ETL (si ETLing)
  • tiempo que lleva enviar registros a un sistema remoto y restaurarlos (si se utiliza el envío de registros)
  • tamaño de las copias de seguridad
  • tiempo que lleva completar la copia de seguridad
  • cuánto tiempo lleva hacer una restauración (esto podría ser importante algún día ;-)
  • tamaño necesario para tempdb
  • rendimiento de los desencadenantes (para tablas insertadas y eliminadas que se almacenan en tempdb)
  • rendimiento del control de versiones de filas (si se usa SNAPSHOT ISOLATION, ya que el almacén de versiones está en tempdb)
  • capacidad de obtener nuevo espacio en disco cuando el CFO dice que el año pasado gastaron $ 1 millón en una SAN y, por lo tanto, no autorizarán otros $ 250k para almacenamiento adicional
  • tiempo que lleva realizar las operaciones INSERTAR y ACTUALIZAR
  • cantidad de tiempo que lleva hacer el mantenimiento del índice
  • etc, etc, etc.

El desperdicio de espacio tiene un enorme efecto en cascada en todo el sistema. Escribí un artículo con detalles explícitos sobre este tema: ¡El disco es barato! ORLY? (Se requiere registro gratuito; lo siento, no controlo esa política).

Tercero: si bien algunas respuestas se centran incorrectamente en el aspecto "esta es una aplicación pequeña", y algunas sugieren correctamente "usar lo que es apropiado", ninguna de las respuestas ha proporcionado una guía real para el OP. Un detalle importante mencionado en la pregunta es que esta es una página web para su escuela. ¡Excelente! Entonces podemos sugerir que:

  • Los campos para los nombres de estudiantes y / o profesores probablemente deberían ser NVARCHARya que, con el tiempo, es cada vez más probable que los nombres de otras culturas aparezcan en esos lugares.
  • ¿Pero para la dirección y los nombres de las ciudades? No se especificó el propósito de la aplicación (hubiera sido útil), pero suponiendo que los registros de direcciones, si los hubiera, pertenezcan solo a una región geográfica particular (es decir, un solo idioma / cultura), utilícelos VARCHARcon la página de códigos apropiada (que se determina a partir de la clasificación del campo).
  • Si almacenar códigos de Estado y / o ISO del país (sin necesidad de almacenar INT/ TINYINTlongitud ya que los códigos ISO son fijos, legible por humanos, y así, :) uso estándar CHAR(2)de códigos de dos letras, y CHAR(3)si se usa 3 códigos de letras. Y considere usar una intercalación binaria como Latin1_General_100_BIN2.
  • Si almacena códigos postales (es decir, códigos postales), utilícelos VARCHARya que es un estándar internacional no utilizar nunca ninguna letra fuera de AZ. Y sí, aún use VARCHARincluso si solo almacena códigos postales de EE. UU. Y no INT, ya que los códigos postales no son números, son cadenas, y algunos de ellos tienen un "0" inicial. Y considere usar una intercalación binaria como Latin1_General_100_BIN2.
  • Si almacena direcciones de correo electrónico y / o URL, úselos NVARCHARya que ambos pueden contener caracteres Unicode.
  • y así....

Cuarto: ahora que tiene NVARCHARdatos que ocupan el doble de espacio del necesario para los datos que encajan bien VARCHAR("encaja bien = = no se convierte en"? ") Y de alguna manera, como por arte de magia, la aplicación creció y ahora hay millones de registros en al menos uno de estos campos donde la mayoría de las filas son ASCII estándar, pero algunas contienen caracteres Unicode, por lo que debe mantenerlas NVARCHAR, considere lo siguiente:

  1. Si está utilizando SQL Server 2008 - 2016 RTM y está en Enterprise Edition, O si está utilizando SQL Server 2016 SP1 (que hizo que la compresión de datos esté disponible en todas las ediciones) o posterior, puede habilitar la compresión de datos . La compresión de datos puede (pero no "siempre") comprimir datos Unicode en NCHARy NVARCHARcampos. Los factores determinantes son:

    1. NCHAR(1 - 4000)y NVARCHAR(1 - 4000)use el Esquema de compresión estándar para Unicode , pero solo a partir de SQL Server 2008 R2, ¡Y solo para datos IN ROW, no OVERFLOW! Esto parece ser mejor que el algoritmo de compresión ROW / PAGE normal.
    2. NVARCHAR(MAX)y XML(y supongo que también VARBINARY(MAX), TEXTy NTEXT) los datos que están EN LA FILA (no fuera de la fila en las páginas LOB o OVERFLOW) pueden al menos comprimirse en PÁGINA, pero no en ROW. Por supuesto, la compresión PAGE depende del tamaño del valor en fila: probé con VARCHAR (MAX) y vi que las filas de 6000 caracteres / bytes no se comprimirían, pero las filas de 4000 caracteres / bytes sí.
    3. Cualquier dato OFF ROW, LOB u OVERLOW = ¡Sin compresión para usted!
  2. Si usa SQL Server 2005 o 2008 - 2016 RTM y no está en Enterprise Edition, puede tener dos campos: uno VARCHARy uno NVARCHAR. Por ejemplo, supongamos que está almacenando URL que en su mayoría son caracteres ASCII básicos (valores 0-127) y, por lo tanto, se ajustan VARCHAR, pero a veces tienen caracteres Unicode. Su esquema puede incluir los siguientes 3 campos:

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

    En este modelo solo SELECCIONA de la [URL]columna calculada. Para insertar y actualizar, usted determina qué campo usar al ver si la conversión altera el valor entrante, que debe ser de NVARCHARtipo:

    INSERT INTO TableName (..., URLa, URLu)
    VALUES (...,
            IIF (CONVERT(VARCHAR(2048), @URL) = @URL, @URL, NULL),
            IIF (CONVERT(VARCHAR(2048), @URL) <> @URL, NULL, @URL)
           );
  3. Puede GZIP valores entrantes VARBINARY(MAX)y luego descomprimir al salir:

    • Para SQL Server 2005-2014: puede usar SQLCLR. SQL # (una biblioteca SQLCLR que escribí) viene con Util_GZip y Util_GUnzip en la versión gratuita
    • Para SQL Server 2016 y versiones posteriores: puede usar las funciones COMPRESSy DECOMPRESSincorporadas, que también son GZip.
  4. Si usa SQL Server 2017 o más reciente, puede considerar convertir la tabla en un Índice de almacén de columnas en clúster.

  5. Si bien aún no es una opción viable, SQL Server 2019 presenta soporte nativo para UTF-8 en VARCHAR/ CHARdatatypes. Actualmente hay demasiados errores para que pueda usarse, pero si se corrigen, esta es una opción para algunos escenarios. Consulte mi publicación, " Soporte nativo de UTF-8 en SQL Server 2019: ¿Salvador o falso profeta? ", Para un análisis detallado de esta nueva característica.

Solomon Rutzky
fuente
77
Aplauso lento. Simplemente sorprendido de que "siempre use nvarchar" obtuvo 140 votos y esto no. Gran trabajo en esta publicación.
schizoid04
1
@ schizoid04 Gracias. Para ser justos, la respuesta aceptada se publicó 7 años antes que la mía, por lo que hay mucho tráfico que votó (y / o varios otros) que nunca volvió a reevaluarse. Aún así, proporciona un contrapunto muy sólido a la teoría de la "sabiduría de la multitud" que impulsa los foros basados ​​en votos. Hay demasiada información errónea por ahí. Por ejemplo, esto en DBA.SE. La otra respuesta, aceptada antes de publicar la mía, es "correcta" por la más estrecha de las definiciones, engañosa, y contiene información que desapruebo en la mía, pero aún así supera la mía.
Solomon Rutzky
22

Para su aplicación, nvarchar está bien porque el tamaño de la base de datos es pequeño. Decir "siempre usar nvarchar" es una gran simplificación excesiva. Si no necesita almacenar cosas como Kanji u otros personajes locos, use VARCHAR, usará mucho menos espacio. Mi predecesor en mi trabajo actual diseñó algo usando NVARCHAR cuando no era necesario. Recientemente lo cambiamos a VARCHAR y ahorramos 15 GB solo en esa tabla (fue muy escrito). Además, si tiene un índice en esa tabla y desea incluir esa columna o hacer un índice compuesto, acaba de aumentar el tamaño del archivo de índice.

Solo sea reflexivo en su decisión; En el desarrollo de SQL y en las definiciones de datos, rara vez parece haber una "respuesta predeterminada" (aparte de evitar los cursores a toda costa, por supuesto).

WebMasterP
fuente
10

Dado que su aplicación es pequeña, esencialmente no hay un aumento de costos apreciable al usar nvarchar sobre varchar, y se ahorra posibles dolores de cabeza en el futuro si necesita almacenar datos Unicode.

tbreffni
fuente
8

Generalmente hablando; Comience con el tipo de datos más caro que tenga las menores restricciones. Ponlo en producción . Si el rendimiento comienza a ser un problema, averigüe qué se almacena realmente en esas nvarcharcolumnas. ¿Hay algún personaje allí que no encaja varchar? Si no, cambie a varchar. No intentes optimizar previamente antes de saber dónde está el dolor. Supongo que la elección entre nvarchar / varchar no es lo que va a ralentizar su aplicación en el futuro previsible. Habrá otras partes de la aplicación donde el ajuste del rendimiento le dará mucho más por los dólares .

Kjetil Klaussen
fuente
7

Durante los últimos años, todos nuestros proyectos han utilizado NVARCHAR para todo, ya que todos estos proyectos son multilingües. Los datos importados de fuentes externas (por ejemplo, un archivo ASCII, etc.) se convierten a Unicode antes de insertarse en la base de datos.

Todavía no he encontrado ningún problema relacionado con el rendimiento de los índices más grandes, etc. Los índices usan más memoria, pero la memoria es barata.

Si usa procedimientos almacenados o construye SQL sobre la marcha, asegúrese de que todas las constantes de cadena tengan el prefijo N (por ejemplo, SET @foo = N'Hello world. ';) Para que la constante también sea Unicode. Esto evita cualquier conversión de tipo de cadena en tiempo de ejecución.

YMMV.

devstuff
fuente
44
Probablemente no tenga varios cientos de millones de registros en las tablas con las que está trabajando. Estoy de acuerdo en que para la mayoría de las aplicaciones, el valor predeterminado de nvarchar está bien, pero no todas.
Brandon Moore
7

Puedo hablar por experiencia sobre esto, tenga cuidado nvarchar. A menos que lo requiera absolutamente, este tipo de campo de datos destruye el rendimiento en bases de datos más grandes. Heredé una base de datos que me dolía en términos de rendimiento y espacio. ¡Pudimos reducir el tamaño de una base de datos de 30GB en un 70%! Se hicieron algunas otras modificaciones para ayudar con el rendimiento, pero estoy seguro de que también han varcharayudado significativamente con eso. Si su base de datos tiene el potencial de aumentar las tablas a más de un millón de registros, manténgase alejado nvarchara toda costa.

JA
fuente
4

Trato con frecuencia esta pregunta en el trabajo:

  • Fuentes FTP de inventario y precios: las descripciones de los artículos y otros textos estaban en nvarchar cuando varchar funcionaba bien. La conversión de estos a varchar redujo el tamaño del archivo casi a la mitad y realmente ayudó con las cargas.

  • El escenario anterior funcionó bien hasta que alguien puso un carácter especial en la descripción del artículo (tal vez marca registrada, no recuerdo)

Todavía no uso nvarchar cada vez sobre varchar. Si hay alguna duda o potencial para caracteres especiales, uso nvarchar. Me parece que uso varchar principalmente cuando tengo el 100% de control de lo que está poblando el campo.

K Richard
fuente
3

¿Por qué, en toda esta discusión, no se ha mencionado UTF-8? Ser capaz de almacenar el intervalo completo de caracteres unicode no significa que uno deba asignar siempre dos bytes por carácter (o "punto de código" para usar el término UNICODE). Todo el ASCII es UTF-8. ¿SQL Server comprueba los campos VARCHAR () de que el texto es ASCII estricto (es decir, el byte superior bit cero)? Espero que no.

Si luego desea almacenar unicode y desea compatibilidad con aplicaciones antiguas de ASCII, creo que usar VARCHAR () y UTF-8 sería la bala mágica: solo usa más espacio cuando lo necesita.

Para aquellos de ustedes que no estén familiarizados con UTF-8, ¿podría recomendarles una cartilla ?

Tevya
fuente
2
Lo que sugiere podría funcionar para algunas aplicaciones, pero también se debe considerar el impacto de una capa de codificación adicional en la forma en que se procesa el texto SQL. En particular, se efectuarán las intercalaciones, la búsqueda y la coincidencia de patrones. Y si los informes se ejecutan en la base de datos, las herramientas de informes estándar no interpretarán correctamente los caracteres de varios bytes. Y se pueden efectuar importaciones y exportaciones a granel. Creo que, a largo plazo, este esquema puede ser más problemático de lo que vale.
Jeffrey L Whitledge
1
No es posible almacenar UTF-8 en columnas VARCHAR. MSSQL siempre convertirá sus datos UTF-8 a la clasificación de columnas. Si arruina la clasificación (como intentar almacenar CP1252 en Latin_1) la conversión no funcionará y terminará con bytes adicionales en sus datos. Puede parecer que funciona bien cuando convierte latin_1 a UTF-8 (en el lado de la aplicación) y de nuevo a latin_1 (lado de la base de datos), pero es solo una ilusión. Puede escabullirse mediante la conversión automática de la base de datos a la clasificación de su columna utilizando freetds y configurando el protocolo en algo menor que 7, pero pierde la capacidad de consultar nvarchar.
chugadie
1
@chugadie y Tevya: esta respuesta es un poco absurda. SQL Server solo usa UCS-2 / UTF-16 para almacenar datos Unicode (es decir, XML y Ntipos prefijados). No tiene la opción de usar UTF-8. Además, las codificaciones Unicode (UTF-8, UCS-2 / UTF-16 y UTF-32) no se pueden aplicar a los campos VARCHAR.
Solomon Rutzky
2

Habrá casos excepcionales en los que desee restringir deliberadamente el tipo de datos para asegurarse de que no contenga caracteres de un determinado conjunto. Por ejemplo, tuve un escenario en el que necesitaba almacenar el nombre de dominio en una base de datos. La internacionalización de los nombres de dominio no era confiable en ese momento, por lo que era mejor restringir la entrada en el nivel base y ayudar a evitar posibles problemas.

Chris Halcrow
fuente
1

Si está utilizando NVARCHARsolo porque un procedimiento almacenado en el sistema lo requiere, la ocurrencia más frecuente es inexplicable sp_executesqly su SQL dinámico es muy largo, sería mejor desde la perspectiva del rendimiento realizar todas las manipulaciones de cadenas (concatenación, reemplazo, etc.) al VARCHARconvertir el resultado final NVARCHARy alimentarlo en el parámetro proc. Así que no, ¡no lo uses siempre NVARCHAR!

ajeh
fuente