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

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

Pergunta

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

  1. Usar varchar a menos que você lide com muitos dados internacionalizados, use nvarchar.
  2. Apenas use nvarchar para tudo.

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

Foi útil?

Solução

Sempre use nvarchar.

Talvez você nunca precise dos caracteres de byte duplo para a maioria dos aplicativos.No entanto, se você precisar oferecer suporte a linguagens de byte duplo e tiver suporte apenas a byte único no esquema do seu banco de dados, será muito caro voltar e modificar todo o seu aplicativo.

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

Outras dicas

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

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

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

Editar:

Pergunta recente do SO destacando quão ruim o desempenho do nvarchar pode ser ...

O SQL Server usa alta CPU ao pesquisar dentro de strings nvarchar

Ser consistente!JOIN-ing um VARCHAR para NVARCHAR tem um grande impacto no desempenho.

nvarchar terá sobrecarga significativa em memória, armazenamento, conjunto de trabalho e indexação, portanto, se as especificações ditarem que realmente terá nunca seja necessário, não se preocupe.

Eu não teria uma regra rígida e rápida de "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, há muitos casos de colunas em que eu certamente faria essa pergunta com antecedência e, se não obtivesse uma resposta rápida e definitiva imediatamente, faria a coluna nvarchar.

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

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

Hesito em acrescentar aqui mais uma resposta, pois já existem algumas, mas é necessário salientar alguns pontos que não foram apresentados ou que não foram apresentados de forma clara.

Primeiro: Fazer não sempre use NVARCHAR.Essa é uma atitude/abordagem muito perigosa e muitas vezes dispendiosa.E não é melhor dizer "Nunca usar cursores", uma vez que às vezes eles são o meio mais eficiente de resolver um problema específico, e a solução comum de fazer um WHILE loop quase sempre será mais lento que um apropriadamente feito Cursor.

A única vez que você deve usar o termo “sempre” é quando aconselha “fazer sempre o que é melhor para a situação”.É verdade que isso muitas vezes é difícil de determinar, especialmente quando se tenta equilibrar os ganhos de curto prazo no tempo de desenvolvimento (gerente:"precisamos desse recurso - que você não conhecia até agora - há uma semana!") com custos de manutenção de longo 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 X que não tem flexibilidade?Não podemos nos dar ao luxo de um ou dois sprints para consertar isso.O que podemos fazer em uma semana para que possamos voltar aos nossos 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 certas 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 que leva 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 restaurá-los (se estiver usando Log Shipping)
  • tamanho dos backups
  • tempo necessário para concluir o backup
  • quanto tempo leva para fazer uma restauração (isso pode ser importante algum dia ;-)
  • tamanho necessário para tempdb
  • desempenho de gatilhos (para tabelas inseridas e excluídas que estão armazenadas em tempdb)
  • desempenho do controle de versão de linha (se estiver usando SNAPSHOT ISOLATION, já que o armazenamento de versão está em tempdb)
  • capacidade de obter novo espaço em disco quando o CFO diz que eles gastaram US$ 1 milhão em uma SAN no ano passado e, portanto, não autorizarão outros US$ 250 mil para armazenamento adicional
  • tempo necessário para realizar operações INSERT e UPDATE
  • quanto tempo leva para fazer a manutenção do índice
  • etc, etc, etc.

Desperdiçar espaço tem um enorme efeito cascata em todo o sistema.Escrevi um artigo detalhando explicitamente este tópico: O disco é barato!ORLY? (é necessário registro gratuito;desculpe, eu não controlo essa política).

Terceiro: Embora algumas respostas se concentrem incorretamente no aspecto "este é um aplicativo pequeno" e algumas sugiram corretamente "usar o que é apropriado", nenhuma das respostas forneceu orientação real ao OP.Um detalhe importante mencionado na pergunta é que esta é uma página web da escola.Ótimo!Então podemos sugerir que:

  • Os campos para nomes de alunos e/ou professores devem provavelmente ser NVARCHAR já que, com o tempo, é cada vez mais provável que nomes de outras culturas apareçam nesses lugares.
  • Mas para endereços e nomes de cidades?A finalidade do aplicativo não foi declarada (teria sido útil), mas presumindo que os registros de endereço, se houver, pertencem apenas a uma região geográfica específica (ou seja,um único idioma/cultura), então use VARCHAR com a página de código apropriada (que é determinada no agrupamento do campo).
  • Se estiver armazenando códigos ISO de estado e/ou país (não há necessidade de armazenar INT / TINYINT já que os códigos ISO têm comprimento fixo, são legíveis por humanos e, bem, padrão :) use CHAR(2) para códigos de duas letras e CHAR(3) se estiver usando códigos de 3 letras.E considere usar um agrupamento binário como Latin1_General_100_BIN2.
  • Se armazenar códigos postais (ou seja,códigos postais), use VARCHAR já que é um padrão internacional nunca usar nenhuma letra fora de A-Z.E sim, ainda uso VARCHAR mesmo que armazene apenas códigos postais dos EUA e não INT, já que os códigos postais não são números, são strings e alguns deles têm um "0" inicial.E considere usar um agrupamento binário como Latin1_General_100_BIN2.
  • Se estiver armazenando endereços de e-mail e/ou URLs, use NVARCHAR já que ambos agora podem conter caracteres Unicode.
  • e assim por diante....

Quarto: Agora que você tem NVARCHAR dados ocupando o dobro do espaço necessário para dados que se ajustam perfeitamente VARCHAR ("se encaixa perfeitamente" = não se transforma em "?") e de alguma forma, como num passe de mágica, o aplicativo cresceu e agora existem milhões de registros em pelo menos um desses campos onde maioria linhas são ASCII padrão, mas algumas contêm caracteres Unicode, então você deve manter NVARCHAR, considere o seguinte:

  1. Se você estiver usando o SQL Server 2008 - 2016 RTM e estiver na 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, você poderá ativar Compressão de dados.A compactação de dados pode (mas não "sempre") compactar dados Unicode em NCHAR e NVARCHAR Campos.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 a partir do SQL Server 2008 R2, E apenas para dados IN ROW, não OVERFLOW!Isso parece ser melhor do que o algoritmo de compactação ROW/PAGE normal.
    2. NVARCHAR(MAX) e XML (e acho que também VARBINARY(MAX), TEXT, e NTEXT) os dados que estão IN ROW (não fora da linha nas páginas LOB ou OVERFLOW) podem pelo menos ser compactados PAGE, mas não LINHA compactada.Obviamente, a compactação PAGE depende do tamanho do valor na linha:Testei com VARCHAR(MAX) e vi que linhas de 6.000 caracteres/byte não seriam compactadas, mas linhas de 4.000 caracteres/byte sim.
    3. Quaisquer dados OFF ROW, LOB ou OVERLOW = Sem compactação para você!
  2. Se estiver usando SQL Server 2005 ou 2008 - 2016 RTM e não na Enterprise Edition, você pode ter dois campos:um VARCHAR e um NVARCHAR.Por exemplo, digamos que você esteja armazenando URLs que são, em sua maioria, caracteres ASCII básicos (valores de 0 a 127) e, portanto, cabem em VARCHAR, mas às vezes possuem caracteres Unicode.Seu esquema pode incluir os três campos a seguir:

      ...
      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ê apenas SELECIONE no [URL] coluna computada.Para inserir e atualizar, você determina qual campo usar verificando se a conversão altera o valor recebido, que deve ser de NVARCHAR tipo:

    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 os valores recebidos em VARBINARY(MAX) e descompacte na saída:

    • Para SQL Server 2005 - 2014:você pode usar SQLCLR. SQL# (uma biblioteca SQLCLR que escrevi) vem com Util_GZip e Util_GUnzip na versão gratuita
    • Para SQL Server 2016 e mais recente:você pode usar o embutido COMPRESS e DECOMPRESS funções, que também são GZip.
  4. Se estiver usando o SQL Server 2017 ou mais recente, você pode tentar transformar a tabela em um Índice Columnstore Clusterizado.

  5. Embora esta ainda não seja uma opção viável, o SQL Server 2019 introduz suporte nativo para UTF-8 em VARCHAR / CHAR tipos de dados.Atualmente existem muitos bugs para que ele possa ser usado, mas se eles forem corrigidos, então esta é uma opção para alguns cenários.Por favor, veja minha postagem, "Suporte nativo a UTF-8 no SQL Server 2019:Salvador ou Falso Profeta?", para uma análise detalhada deste novo recurso.

Como seu aplicativo é pequeno, essencialmente não há aumento de custo apreciável no uso de nvarchar em vez de varchar, e você evita possíveis dores de cabeça no futuro se precisar armazenar dados Unicode.

De modo geral;Comece com o tipo de dados mais caro e 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 nesses nvarchar colunas.Tem algum personagem aí que não se encaixaria 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 vai desacelerar seu aplicativo num futuro previsível.Haverá outras partes do aplicativo onde o ajuste de desempenho lhe dará muito mais vale a pena ganhar dinheiro.

Nos últimos anos todos os nossos projetos usaram NVARCHAR para tudo, já que todos esses projetos são multilíngues.Dados importados de fontes externas (por exemplo,um arquivo ASCII, etc.) é convertido para Unicode antes de ser inserido no banco de dados.

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

Quer você use procedimentos armazenados ou construa SQL dinamicamente, certifique-se de que todas as constantes de string sejam prefixadas com N (por exemplo,SET @foo = N'Hello world.';) então a constante também é Unicode.Isso evita qualquer conversão de tipo de string em tempo de execução.

YMMV.

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

Eu lido frequentemente com esta questão no trabalho:

  • Feeds FTP de estoque e preços - As descrições dos itens e outros textos estavam no nvarchar quando o varchar funcionou bem.Convertê-los para varchar reduziu o tamanho do arquivo quase pela metade e realmente ajudou nos uploads.

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

Ainda não uso nvarchar sempre em vez de varchar.Se houver alguma dúvida ou possibilidade de caracteres especiais, eu uso nvarchar.Acho que uso varchar principalmente quando tenho 100% de controle sobre o que está preenchendo o campo.

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

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

Para aqueles que não estão familiarizados com UTF-8, recomendo um iniciador.

Haverá casos excepcionais em que você desejará restringir deliberadamente o tipo de dados para garantir que não contêm caracteres de um determinado conjunto.Por exemplo, tive um cenário em que precisava armazenar o nome de domínio em um banco de dados.A internacionalização de nomes de domínio não era confiável na época, então era melhor restringir a entrada no nível básico e ajudar a evitar possíveis problemas.

Se você estiver usando NVARCHAR só porque um procedimento armazenado do sistema exige isso, sendo a ocorrência mais frequente inexplicavelmente sp_executesql, e 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.) em VARCHAR em seguida, convertendo o resultado final para NVARCHAR e inserindo-o no parâmetro proc.Então não, nem sempre use NVARCHAR!

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top