Quais são as principais diferenças de desempenho entre os tipos de dados SQL Server varchar e nvarchar?

236

Estou trabalhando em um banco de dados para um pequeno aplicativo Web na minha escola usando SQL Server 2005.
Vejo algumas escolas de pensamento sobre a questão do varcharvs nvarchar:

  1. Use, a varcharmenos que você lide com muitos dados internacionalizados, depois use nvarchar.
  2. Basta usar nvarcharpara tudo.

Estou começando a ver os méritos da visão 2. Sei que o nvarchar ocupa duas vezes mais espaço, mas isso não é necessariamente um grande negócio, pois isso só vai armazenar dados para algumas centenas de estudantes. Para mim, parece que seria mais fácil não se preocupar com isso e permitir que tudo usasse o nvarchar. Ou há algo que estou perdendo?

Jason Baker
fonte
pergunta semelhante aqui: stackoverflow.com/questions/312170/… EDIT por le dorfier: que curiosamente chegou exatamente à conclusão oposta.
Booji Boy
6
referência thread muito mais extenso que chegou à conclusão oposta. stackoverflow.com/questions/312170/…
dkretz
2
Jason: Espero que não seja uma solicitação inadequada, mas você pode alterar a resposta aceita para gbn's . A resposta de JoeBarone é terrivelmente errada por vários motivos. Tê-lo "aceito" leva os novatos a fazer más escolhas. É desnecessário e esbanjador "sempre usar NVARCHAR" e pode ter impactos muito negativos no desempenho e nos custos / orçamentos de hardware. Algumas linhas, até alguns milhares, não importam. Como os sistemas crescem mais rapidamente do que as pessoas esperam, a resposta atual aceita é um desserviço à comunidade. Obrigado.
Solomon Rutzky

Respostas:

140

Sempre use nvarchar.

Você pode nunca precisar dos caracteres de byte duplo para a maioria dos aplicativos. No entanto, se você precisar oferecer suporte a idiomas de byte duplo e tiver suporte a byte único no esquema do banco de dados, é muito caro voltar e modificar em todo o aplicativo.

O custo da migração de um aplicativo de varchar para nvarchar será muito mais do que o pouco de espaço em disco extra que você utilizará na maioria dos aplicativos.

Joe Barone
fonte
4
é muito mais difícil voltar e adicionar suporte para mensagens / texto multilíngües, fusos horários, unidades de medida e moeda, para que todos DEVEM sempre codificá-los em seu aplicativo desde o primeiro dia, SEMPRE (mesmo que seja apenas na página inicial da web) aplicativo)!
KM.
82
E quanto ao tamanho do índice, uso de memória etc? Presumo que você sempre use int quando puder usar tinyint também "apenas no caso"?
gbn
99
Sempre codificar / planejar um site multilíngue (quando você não tem idéia de que algum dia precisará dele) é como dizer a todos os jovens adultos que eles devem comprar um grande SUV de 8 lugares que consome gasolina para seu primeiro carro ... afinal de contas , eles podem se casar um dia e ter 6 filhos,. Prefiro aproveitar o desempenho e a eficiência enquanto posso e pagar o preço pela atualização quando / se eu precisar.
EJ Brennan
4
@ cbmeeks: Eu não codigo para o que não sei. Mas se você pode usá-lo sem sucesso notável de desempenho, em seguida, seus bancos de dados não são grandes o suficiente para que importa ...
gbn
60
Normalmente, quando as pessoas começam sua resposta com a palavra "Sempre", você deve ignorar tudo o que vem depois disso. (Observe que eu comecei essa afirmação com a palavra "geralmente")
Brandon Moore
226

Espaço em disco não é o problema ... mas a memória e o desempenho serão. Dobra a leitura da página, tamanho de índice duplo, LIKE estranho e = comportamento constante etc

Você precisa armazenar scripts etc em chinês? Sim ou não...

E do MS BOL " Efeitos de armazenamento e desempenho do Unicode "

Editar :

Pergunta recente do SO que destaca o desempenho ruim do nvarchar ...

O SQL Server usa alta CPU ao pesquisar dentro de cadeias de caracteres nvarchar

gbn
fonte
19
+1, se seu aplicativo for internacional, você terá muitos outros problemas para se preocupar com a pesquisa / substituição do nvarchar: mensagens / textos multilíngues, fusos horários, unidades de medida e moeda
KM.
2
Mas e se você precisar armazenar um nome estrangeiro, como José ou Bjørn?
Qwertie
7
@ Qwertie: então você usa nvarchar. O que você não faz usa desnecessariamente. Esses dois nomes se encaixam no varchar de qualquer maneira
IIRC
6
Dizer que o espaço em disco não é um problema não é verdadeiro para todos. Usamos ingenuamente o nvarchar desnecessariamente em um grande aplicativo bancário com bilhões de registros armazenados por muitos anos. Com um armazenamento caro baseado em SAN com replicação, backup e recuperação de desastre, isso pode realmente resultar em milhões de dólares em custos para nvarchar vs varchar. Sem mencionar que há um grande impacto no desempenho (100%) que precisa ler duas vezes mais bytes do disco para cada leitura.
Codemonkey
2
@ cododeckey, et al: Eu fiz o que pude para resolver a questão do espaço desperdiçado holisticamente no seguinte artigo: O disco é barato! ORLY? (é necessário registro gratuito). O artigo tem como objetivo ajudar a evitar a situação em que a codemonkey se deparou com o armazenamento caro no nível da empresa.
Solomon Rutzky
59

Ser consistente! Juntar um VARCHAR ao NVARCHAR tem um grande impacto no desempenho.

Thomas Harlan
fonte
115
Se você estiver fazendo junções em campos de caracteres, seu banco de dados provavelmente terá problemas piores do que usar nvarchar ou varchar, em geral.
Brandon Moore
@Thomas Harlan Um teste simples demonstra-me que não há diferença tangível entre juntar nvarchara varcharvs converter nvarcharpara varchare juntando-se a varchar. A menos que, claro, você quisesse ser consistente nos tipos de dados da coluna, não na junção.
ajeh
1
@ajeh e Thomas: 1) Os testes "simples" geralmente são enganosos, pois não cobrem variações que causam diferenças de comportamento. 2) Se alguém vê um desempenho drástico na mixagem VARCHARe NVARCHARisso deve ser devido à indexação da VARCHARcoluna, juntamente com o tipo de agrupamento usado para essa coluna (e, portanto, o índice). Abordo este tópico em detalhes na seguinte postagem no blog: Impacto nos índices ao misturar os tipos VARCHAR e NVARCHAR .
Solomon Rutzky 5/17/17
44

O nvarchar terá uma sobrecarga significativa na memória, armazenamento, conjunto de trabalho e indexação; portanto, se as especificações determinarem que realmente nunca será necessário, não se preocupe.

Eu não teria uma regra rígida e rápida "sempre nvarchar" porque pode ser um desperdício completo em muitas situações - particularmente ETL de ASCII / EBCDIC ou identificadores e colunas de código que geralmente são chaves e chaves estrangeiras.

Por outro lado, existem muitos casos de colunas, onde eu faria essa pergunta mais cedo e, se não obtivesse uma resposta rápida e imediata, tornaria a coluna nvarchar.

Cade Roux
fonte
26

Hesito em acrescentar mais uma resposta aqui, pois já existem algumas, mas é preciso ressaltar alguns pontos que ainda não foram feitos ou que não foram feitos claramente.

Primeiro: Do não usar sempre NVARCHAR. Essa é uma atitude / abordagem muito perigosa e muitas vezes dispendiosa. E não é melhor dizer " Nunca use cursores", pois às vezes eles são os meios mais eficientes de resolver um problema específico, e a solução comum de fazer um WHILEloop quase sempre será mais lenta do que um Cursor feito corretamente .

O único momento em que você deve usar o termo "sempre" é quando aconselhado a "sempre fazer o que é melhor para a situação". É óbvio que é difícil determinar, principalmente quando se tenta equilibrar os ganhos de curto prazo no tempo de desenvolvimento (gerente: "precisamos desse recurso - que você não conhecia até agora - uma semana atrás!") Por muito tempo. custos de manutenção a médio prazo (gerente que inicialmente pressionou a equipe para concluir um projeto de 3 meses em um sprint de 3 semanas: "por que estamos tendo esses problemas de desempenho? Como poderíamos ter feito o X sem flexibilidade? Não podemos pagar um sprint ou dois para corrigir isso. O que podemos fazer em uma semana para voltar aos itens prioritários? E, definitivamente, precisamos gastar mais tempo no design para que isso não continue acontecendo! ").

Segundo: a resposta do @ gbn aborda alguns pontos muito importantes a serem considerados ao tomar determinadas decisões de modelagem de dados quando o caminho não está 100% claro. Mas há ainda mais a considerar:

  • tamanho dos arquivos de log de transações
  • tempo necessário para replicar (se estiver usando replicação)
  • tempo que leva para ETL (se ETLing)
  • tempo necessário para enviar logs para um sistema remoto e restaurar (se estiver usando o envio de logs)
  • tamanho dos backups
  • tempo necessário para concluir o backup
  • tempo que leva para fazer uma restauração (isso pode ser importante um dia ;-)
  • tamanho necessário para tempdb
  • desempenho de gatilhos (para tabelas inseridas e excluídas armazenadas no tempdb)
  • desempenho do controle de versão de linha (se estiver usando SNAPSHOT ISOLATION, já que o armazenamento de versão está no tempdb)
  • capacidade de obter novo espaço em disco quando o CFO diz que gastou US $ 1 milhão em uma SAN no ano passado e, portanto, não autorizará outros US $ 250 mil para armazenamento adicional
  • tempo necessário para executar as operações INSERT e UPDATE
  • tempo necessário para fazer a manutenção do índice
  • etc etc etc

A perda de espaço tem um enorme efeito cascata em todo o sistema. Eu escrevi um artigo entrando em detalhes explícitos sobre este tópico: O disco é barato! ORLY? (é necessário registro gratuito; desculpe-me por não controlar essa política).

Terceiro: enquanto algumas respostas estão incorretamente focadas no aspecto "este é um aplicativo pequeno" e outras estão sugerindo corretamente "usar o que é apropriado", nenhuma das respostas forneceu orientações reais ao OP Um detalhe importante mencionado na pergunta é que esta é uma página da web da escola deles. Ótimo! Então, podemos sugerir que:

  • Os campos para nomes de Alunos e / ou Faculdade provavelmente devem ser NVARCHAR, pois, com o tempo, é cada vez mais provável que nomes de outras culturas sejam exibidos nesses locais.
  • Mas para endereço e nomes de cidades? O objetivo do aplicativo não foi declarado (teria sido útil), mas assumindo que os registros de endereço, se houver, pertencem apenas a uma região geográfica específica (por exemplo, um único idioma / cultura) e, em seguida, use VARCHARa Página de Código apropriada (que é determinado a partir do agrupamento do campo).
  • Se o armazenamento de códigos ISO do estado e / ou país (não é necessário armazenar INT/ TINYINTjá que os códigos ISO são de comprimento fixo, legíveis por humanos e padrão): use CHAR(2)para códigos de duas letras e CHAR(3)se estiver usando três códigos de letras. E considere usar um agrupamento binário como Latin1_General_100_BIN2.
  • Se estiver armazenando códigos postais (por exemplo, CEP), use, VARCHARpois é um padrão internacional nunca usar nenhuma letra fora de AZ. E sim, ainda use, VARCHARmesmo que apenas armazene códigos postais dos EUA e não INT, pois os códigos postais não são números, são cadeias de caracteres e alguns deles têm um "0" inicial. E considere usar um agrupamento binário como Latin1_General_100_BIN2.
  • Se você estiver armazenando endereços de e-mail e / ou URLs, use os NVARCHARdois agora que podem conter caracteres Unicode.
  • e assim por diante....

Quarto: agora que você tem NVARCHARdados que ocupam o dobro do espaço necessário para dados que se encaixam perfeitamente VARCHAR("se encaixa perfeitamente" = não se transforma em "?") E, de alguma forma, como por mágica, o aplicativo cresceu e agora existem milhões de registros em pelo menos um desses campos em que a maioria das linhas é ASCII padrão, mas algumas contêm caracteres Unicode, portanto, você deve mantê- las NVARCHAR, considere o seguinte:

  1. Se você estiver usando o SQL Server 2008 - 2016 RTM e estiver no Enterprise Edition, OU se estiver usando o SQL Server 2016 SP1 (que disponibilizou a compactação de dados em todas as edições) ou mais recente, poderá habilitar a compactação de dados . A compactação de dados pode (mas não "sempre") compactar dados NCHARe NVARCHARcampos Unicode . Os fatores determinantes são:

    1. NCHAR(1 - 4000)e NVARCHAR(1 - 4000)use o Esquema de compactação padrão para Unicode , mas apenas iniciando no SQL Server 2008 R2 E somente para dados IN ROW, não SOB FLUXO! Parece ser melhor que o algoritmo de compactação ROW / PAGE comum.
    2. NVARCHAR(MAX)e XML(e acho que também VARBINARY(MAX), TEXTe NTEXT) dados que estão IN ROW (não fora da linha nas páginas LOB ou OVERFLOW) podem pelo menos ser compactados em PAGE, mas não compactados em ROW. Obviamente, a compactação PAGE depende do tamanho do valor em linha: testei com VARCHAR (MAX) e vi que as linhas de 6000 caracteres / byte não seriam compactadas, mas sim as linhas de 4000 caracteres / byte.
    3. Quaisquer dados OFF ROW, LOB ou OVERLOW = sem compressão para você!
  2. Se você estiver usando o SQL Server 2005 ou 2008 - 2016 RTM e não no Enterprise Edition, poderá ter dois campos: um VARCHARe um NVARCHAR. Por exemplo, digamos que você esteja armazenando URLs que são na sua maioria caracteres ASCII básicos (valores de 0 a 127) e, portanto, se encaixam VARCHAR, mas às vezes têm caracteres Unicode. Seu esquema pode incluir os seguintes 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))
    );

    Neste modelo, você seleciona apenas a partir da [URL]coluna computada. Para inserir e atualizar, você determina qual campo usar, verificando se a conversão altera o valor recebido, que deve ser do NVARCHARtipo:

    INSERT INTO TableName (..., URLa, URLu)
    VALUES (...,
            IIF (CONVERT(VARCHAR(2048), @URL) = @URL, @URL, NULL),
            IIF (CONVERT(VARCHAR(2048), @URL) <> @URL, NULL, @URL)
           );
  3. Você pode GZIP inserir valores VARBINARY(MAX)e descompactar na saída:

    • Para o SQL Server 2005 - 2014: você pode usar o SQLCLR. SQL # (uma biblioteca SQLCLR que escrevi) vem com Util_GZip e Util_GUnzip na versão gratuita
    • Para o SQL Server 2016 e mais recente: você pode usar o built-in COMPRESSe DECOMPRESSfunções, que também são GZip.
  4. Se você estiver usando o SQL Server 2017 ou mais recente, poderá transformar a tabela em um Índice de armazenamento de colunas em cluster.

  5. Embora essa ainda não seja uma opção viável, o SQL Server 2019 apresenta suporte nativo para UTF-8 em VARCHAR/ CHARtipos de dados. Atualmente, existem muitos bugs com ele para serem usados, mas se forem corrigidos, essa é uma opção para alguns cenários. Consulte a minha publicação, " Suporte nativo a UTF-8 no SQL Server 2019: Salvador ou Falso Profeta? ", Para uma análise detalhada desse novo recurso.

Solomon Rutzky
fonte
7
Aplauso lento. Simplesmente espantado que "sempre use nvarchar" obteve 140 votos e isso não aconteceu. Bom trabalho neste post.
precisa
1
@ schizoid04 Obrigado. Para ser justo, a resposta aceita foi postada sete anos antes da minha, então há muito tráfego que votou nele (e / ou em vários outros) que nunca voltou a reavaliar. Ainda assim, fornece um contraponto muito sólido à teoria da "sabedoria da multidão" que dirige fóruns baseados em votos. Há muita desinformação por aí. Por exemplo, isso no DBA.SE. A outra resposta, aceita antes de publicar a minha, é "correta" pelas definições mais restritas, enganosa e contém informações que eu refuto nas minhas, mas ainda supera as minhas.
Solomon Rutzky
22

Para o seu aplicativo, o nvarchar é bom porque o tamanho do banco de dados é pequeno. Dizer "sempre use nvarchar" é uma grande simplificação. Se você não precisar armazenar coisas como Kanji ou outros personagens malucos, use VARCHAR, ele utilizará muito menos espaço. Meu antecessor no meu trabalho atual projetou algo usando o NVARCHAR quando não era necessário. Recentemente, nós o mudamos para VARCHAR e salvamos 15 GB apenas nessa tabela (ela foi altamente gravada). Além disso, se você tiver um índice nessa tabela e desejar incluir essa coluna ou criar um índice composto, acabou de aumentar o tamanho do arquivo de índice.

Apenas seja atencioso em sua decisão; no desenvolvimento de SQL e nas definições de dados, raramente parece haver uma "resposta padrão" (além de evitar cursores a todo custo, é claro).

WebMasterP
fonte
10

Como seu aplicativo é pequeno, não há essencialmente um aumento considerável de custo no uso de nvarchar sobre varchar, e você economiza dores de cabeça em potencial no caminho, se precisar armazenar dados unicode.

tbreffni
fonte
8

De um modo geral; Comece com o tipo de dados mais caro que tenha menos restrições. Coloque em produção . Se o desempenho começar a ser um problema, descubra o que realmente está sendo armazenado nessas nvarcharcolunas. Existe algum personagem que não se encaixe varchar? Caso contrário, mude para varchar. Não tente pré-otimizar antes de saber onde está a dor. Meu palpite é que a escolha entre nvarchar / varchar não é o que desacelerará seu aplicativo em um futuro previsível. Haverá outras partes do aplicativo em que o ajuste de desempenho fornecerá muito mais dinheiro para o dinheiro .

Kjetil Klaussen
fonte
7

Nos últimos anos, todos os nossos projetos usaram o NVARCHAR para tudo, pois todos eles são multilíngues. Os dados importados de fontes externas (por exemplo, um arquivo ASCII, etc.) são convertidos para Unicode antes de serem inseridos no banco de dados.

Ainda não encontrei problemas relacionados ao desempenho dos índices maiores, etc. Os índices usam mais memória, mas a memória é barata.

Se você usa procedimentos armazenados ou constrói SQL rapidamente, assegure-se de que todas as constantes de sequência sejam prefixadas com N (por exemplo, SET @foo = N'Hello world. ';) Para que a constante também seja Unicode. Isso evita qualquer conversão de tipo de string em tempo de execução.

YMMV.

devstuff
fonte
4
Você provavelmente não possui centenas de milhões de registros nas tabelas com as quais está trabalhando. Concordo que, para a maioria dos aplicativos, o padrão é nvarchar, mas não todos.
Brandon Moore
7

Eu posso falar por experiência própria, cuidado com isso nvarchar. A menos que seja absolutamente necessário, esse tipo de campo de dados destrói o desempenho em um banco de dados maior. Eu herdei um banco de dados que estava prejudicando em termos de desempenho e espaço. Conseguimos reduzir em 70% um banco de dados de 30 GB! Houve algumas outras modificações feitas para ajudar no desempenho, mas tenho certeza de que elas também varcharajudaram significativamente. Se seu banco de dados tem o potencial de aumentar as tabelas para mais de um milhão de registros, fique longe nvarchara todo custo.

JA
fonte
4

Lido com essa questão no trabalho com frequência:

  • Feeds FTP de inventário e preços - descrições de itens e outros textos estavam em nvarchar quando varchar funcionou bem. Convertê-los para varchar reduziu o tamanho do arquivo quase pela metade e realmente ajudou nos envios.

  • O cenário acima funcionou bem até que alguém colocou um caractere especial na descrição do item (talvez marca comercial, não lembre)

Eu ainda não uso o nvarchar todas as vezes sobre o varchar. Se houver alguma dúvida ou potencial para caracteres especiais, eu uso o nvarchar. Acho que uso varchar principalmente quando estou no controle de 100% do que está preenchendo o campo.

K Richard
fonte
3

Por que, em toda essa discussão, não houve menção ao UTF-8? Ser capaz de armazenar a extensão unicode completa de caracteres não significa que é preciso sempre alocar dois bytes por caractere (ou "ponto de código" para usar o termo UNICODE). Todo o ASCII é UTF-8. O SQL Server verifica nos campos VARCHAR () que o texto é estrito ASCII (isto é, bit de byte superior zero)? Eu espero que não.

Se você deseja armazenar unicode e compatibilidade com aplicativos antigos, apenas ASCII, acho que usar VARCHAR () e UTF-8 seria a mágica: ele só usa mais espaço quando necessário.

Para aqueles que não conhecem o UTF-8, recomendo uma cartilha .

Tevya
fonte
2
O que você está sugerindo pode funcionar para alguns aplicativos, mas é preciso também considerar o impacto de uma camada de codificação extra na maneira como o texto SQL é processado. Em particular, agrupamentos, pesquisas e correspondência de padrões serão efetuados. E se os relatórios forem executados no banco de dados, as ferramentas de relatório padrão não interoperarão os caracteres de vários bytes corretamente. E importações e exportações a granel podem ser efetuadas. Penso que, a longo prazo, esse esquema pode ser mais problemático do que vale a pena.
Jeffrey L Whitledge
1
Não é possível armazenar UTF-8 nas colunas VARCHAR. O MSSQL sempre converterá seus dados UTF-8 no agrupamento de colunas. Se você estragar o agrupamento (como tentar armazenar o CP1252 no Latin_1), a conversão não funcionará e você terá bytes extras nos seus dados. Pode parecer funcionar bem quando você converte latin_1 em UTF-8 (no lado do aplicativo) e volta novamente para latin_1 (lado db), mas é apenas uma ilusão. Você pode esgueirar-se pela conversão automática do banco de dados para o agrupamento de colunas usando freetds e configurando o protocolo para algo menor que 7, mas perde a capacidade de consultar o nvarchar.
Chugadie
1
@chugadie e Tevya: esta resposta é um pouco absurda. O SQL Server usa apenas UCS-2 / UTF-16 para armazenar dados Unicode (por Nexemplo, tipos XML e prefixados). Você não tem a opção de usar UTF-8. Além disso, as codificações Unicode (UTF-8, UCS-2 / UTF-16 e UTF-32) não podem ser aplicadas aos campos VARCHAR.
Solomon Rutzky
2

Haverá casos excepcionais em que você desejará restringir deliberadamente o tipo de dados para garantir que ele não contenha caracteres de um determinado conjunto. Por exemplo, eu tive um cenário em que precisava armazenar o nome de domínio em um banco de dados. A internacionalização para nomes de domínio não era confiável no momento; portanto, era melhor restringir a entrada no nível base e ajudar a evitar possíveis problemas.

Chris Halcrow
fonte
1

Se você estiver usando NVARCHARapenas porque um procedimento armazenado do sistema exige isso, a ocorrência mais frequente sendo inexplicavelmente sp_executesqle seu SQL dinâmico for muito longo, seria melhor do ponto de vista do desempenho fazer todas as manipulações de string (concatenação, substituição etc.) e VARCHARdepois converter o resultado final NVARCHARe alimentá-lo no parâmetro proc. Então não, nem sempre use NVARCHAR!

ajeh
fonte