Existe uma diferença de desempenho REAL entre as chaves primárias INT e VARCHAR?

174

Existe uma diferença de desempenho mensurável entre o uso do INT e do VARCHAR como chave primária no MySQL? Eu gostaria de usar o VARCHAR como a chave primária para listas de referência (pense nos Estados Unidos, códigos de país) e um colega de trabalho não se moverá no INT AUTO_INCREMENT como chave primária para todas as tabelas.

Meu argumento, conforme detalhado aqui , é que a diferença de desempenho entre INT e VARCHAR é insignificante, uma vez que toda referência de chave estrangeira INT exigirá que um JOIN compreenda a referência, uma chave VARCHAR apresentará diretamente as informações.

Então, alguém tem experiência com esse caso de uso específico e as preocupações de desempenho associadas a ele?

Jake McGraw
fonte
3
Fiz um post com a resposta "não" com alguns detalhes dos testes que executei ... mas esse era o SQL Server, não o MySQL. Então eu apaguei minha resposta.
Timothy Khouri
17
@ Timothy - você não deveria ter excluído. Eu estava no processo de votar. A maioria dos servidores de banco de dados SQL possui planejadores de consulta e gargalos de desempenho semelhantes.
Paul Tomblin 01/12/08
9
@ Timothy, repita seus resultados.
Jake McGraw
2
Muitos comentários e respostas assumem que as chaves existem para serem usadas para junções. Eles não são. As chaves devem ser usadas para consistência dos dados - para evitar linhas duplicadas (mais de uma linha representando a mesma entidade). Qualquer coluna (ou conjunto de colunas) pode ser usada em uma junção e para garantir que a junção seja de um para zero ou muitas coluna (s) simplesmente precisam ser únicas. Qualquer índice exclusivo garante isso e não precisa ser significativo.
Charles Bretana

Respostas:

78

Você deve enfatizar que pode evitar um certo número de consultas unidas usando o que é chamado de chave natural em vez de uma chave substituta . Somente você pode avaliar se o benefício disso é significativo em seu aplicativo.

Ou seja, você pode medir as consultas em seu aplicativo que são as mais importantes a serem rápidas, porque elas trabalham com grandes volumes de dados ou são executadas com muita frequência. Se essas consultas se beneficiarem da eliminação de uma associação e não sofrerem o uso de uma chave primária varchar, faça-o.

Não use nenhuma das estratégias para todas as tabelas no seu banco de dados. É provável que, em alguns casos, uma chave natural seja melhor, mas em outros casos uma chave substituta seja melhor.

Outras pessoas afirmam que é raro na prática uma chave natural nunca mudar ou ter duplicatas; portanto, chaves substitutas geralmente valem a pena.

Bill Karwin
fonte
3
E, às vezes, (IMHO, muitas vezes), ambos é melhor, o substituto para o uso de referências FK em outras tabelas, e por junta, ea chave natural para garantir a consistência dos dados
Charles Bretana
@CharlesBretana Isso é interessante. O uso de uma chave natural para a consistência dos dados ao longo do lado do FK é uma prática comum? Meu primeiro pensamento foi que o armazenamento extra que seria necessário em tabelas grandes talvez não valesse a pena. Qualquer informação é apreciada. FYI - Eu tenho um fundo de programação decente, mas a minha experiência SQL é limitado principalmente para SELECT consultas
Rob
2
@CharlesBretana Quando leio "armazene os dois", acho que "redundância" e "não normalizado", o que equivale a "Esse material pode ser estragado" e "Tenho que garantir que ambos sejam alterados, se algum deles for alterado". Se você tiver redundância, deve haver um motivo muito bom (como desempenho completamente inaceitável), porque a redundância sempre é possível para que seus dados se tornem inconsistentes.
Jpmc26
3
@ jpmc26, não há absolutamente nenhum problema de redundância ou normalização envolvido. Uma chave substituta não tem conexão significativa com os valores em uma chave natural, portanto, nunca deve precisar ser alterada. Quanto à normalização, de quais problemas você está falando? A normalização se aplica a atributos significativos de uma relação; o valor numérico de uma chave substituta (na verdade, o próprio conceito de uma chave substituta) fica completamente fora do contexto de qualquer normalização.
Charles Bretana
1
E para responder sua outra pergunta, especificamente sobre uma tabela de estados, se você tivesse uma chave substituta nessa tabela, com valores, por exemplo, de 1 a 50 de frpom, mas você NÃO colocou outro índice ou chave exclusiva no código postal do estado, (e, na minha opinião, também no nome do estado), o que impede alguém de inserir duas linhas com valores-chave substitutos diferentes, mas com o mesmo código postal e / ou nome do estado? Como o aplicativo cliente lidaria com isso se houvesse duas linhas com 'NJ', 'New Jersey'? As chaves naturais garantem a consistência dos dados!
22817 Charles Britana
81

Não é sobre desempenho. É sobre o que faz uma boa chave primária. Único e imutável ao longo do tempo. Você pode pensar que uma entidade como um código de país nunca muda ao longo do tempo e seria um bom candidato a uma chave primária. Mas a experiência amarga é que raramente é assim.

INT AUTO_INCREMENT atende à condição "única e imutável ao longo do tempo". Daí a preferência.

Steve McLeod
fonte
25
Verdade. Um dos meus maiores bancos de dados tem entradas para a Iugoslávia e a União Soviética. Fico feliz que eles não sejam chaves primárias.
Paul Tomblin 01/12/2008
8
@ Steve, então por que o ANSI SQL suporta sintaxe para ON UPDATE CASCADE?
Bill Karwin
5
Imutabilidade não é um requisito de uma chave. Em qualquer caso, as chaves substitutas às vezes também mudam. Nada de errado em mudar de chave, se necessário.
Nvogel
9
Paul, então você mudou a União Soviética para a Rússia no seu banco de dados? E fingir que SU nunca existe? E todas as referências à SU agora apontam para a Rússia?
Dainius
6
@ alga Eu nasci em SU, então eu sei o que é.
Dainius
52

Fiquei um pouco irritado com a falta de referências para este online, então eu mesmo fiz um teste.

Observe, porém, que eu não faço isso regularmente, portanto, verifique minhas configurações e etapas quanto a fatores que possam ter influenciado os resultados sem querer e publique suas preocupações nos comentários.

A configuração foi a seguinte:

  • CPU Intel® Core ™ i7-7500U a 2.70GHz × 4
  • 15,6 GiB de RAM, dos quais eu garanti que cerca de 8 GB estavam livres durante o teste.
  • Unidade SSD de 148,6 GB, com bastante espaço livre.
  • Ubuntu 16.04 de 64 bits
  • MySQL Ver 14.14 Distrib 5.7.20, para Linux (x86_64)

As mesas:

create table jan_int (data1 varchar(255), data2 int(10), myindex tinyint(4)) ENGINE=InnoDB;
create table jan_int_index (data1 varchar(255), data2 int(10), myindex tinyint(4), INDEX (myindex)) ENGINE=InnoDB;
create table jan_char (data1 varchar(255), data2 int(10), myindex char(6)) ENGINE=InnoDB;
create table jan_char_index (data1 varchar(255), data2 int(10), myindex char(6), INDEX (myindex)) ENGINE=InnoDB;
create table jan_varchar (data1 varchar(255), data2 int(10), myindex varchar(63)) ENGINE=InnoDB;
create table jan_varchar_index (data1 varchar(255), data2 int(10), myindex varchar(63), INDEX (myindex)) ENGINE=InnoDB;

Em seguida, preenchi 10 milhões de linhas em cada tabela com um script PHP cuja essência é assim:

$pdo = get_pdo();

$keys = [ 'alabam', 'massac', 'newyor', 'newham', 'delawa', 'califo', 'nevada', 'texas_', 'florid', 'ohio__' ];

for ($k = 0; $k < 10; $k++) {
    for ($j = 0; $j < 1000; $j++) {
        $val = '';
        for ($i = 0; $i < 1000; $i++) {
            $val .= '("' . generate_random_string() . '", ' . rand (0, 10000) . ', "' . ($keys[rand(0, 9)]) . '"),';
        }
        $val = rtrim($val, ',');
        $pdo->query('INSERT INTO jan_char VALUES ' . $val);
    }
    echo "\n" . ($k + 1) . ' millon(s) rows inserted.';
}

Para inttabelas, o bit ($keys[rand(0, 9)])foi substituído por just rand(0, 9)e, para varchartabelas, usei nomes completos de estados dos EUA, sem cortá-los ou estendê-los para 6 caracteres. generate_random_string()gera uma sequência aleatória de 10 caracteres.

Então eu corri no MySQL:

  • SET SESSION query_cache_type=0;
  • Para jan_inttabela:
    • SELECT count(*) FROM jan_int WHERE myindex = 5;
    • SELECT BENCHMARK(1000000000, (SELECT count(*) FROM jan_int WHERE myindex = 5));
  • Para outras tabelas, o mesmo que acima, com myindex = 'califo'para chartabelas e myindex = 'california'para varchartabelas.

Tempos da BENCHMARKconsulta em cada tabela:

  • jan_int: 21.30 seg
  • jan_int_index: 18.79 seg
  • jan_char: 21.70 seg
  • jan_char_index: 18,85 seg
  • jan_varchar: 21.76 seg
  • jan_varchar_index: 18.86 seg

Em relação aos tamanhos de tabela e índice, eis a saída de show table status from janperformancetest;(com algumas colunas não mostradas):

|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Name              | Engine | Version | Row_format | Rows    | Avg_row_length | Data_length | Max_data_length | Index_length | Data_free | Auto_increment | Collation              |
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| jan_int           | InnoDB |      10 | Dynamic    | 9739094 |             43 |   422510592 |               0 |            0 |   4194304 |           NULL | utf8mb4_unicode_520_ci |  
| jan_int_index     | InnoDB |      10 | Dynamic    | 9740329 |             43 |   420413440 |               0 |    132857856 |   7340032 |           NULL | utf8mb4_unicode_520_ci |   
| jan_char          | InnoDB |      10 | Dynamic    | 9726613 |             51 |   500170752 |               0 |            0 |   5242880 |           NULL | utf8mb4_unicode_520_ci |  
| jan_char_index    | InnoDB |      10 | Dynamic    | 9719059 |             52 |   513802240 |               0 |    202342400 |   5242880 |           NULL | utf8mb4_unicode_520_ci |  
| jan_varchar       | InnoDB |      10 | Dynamic    | 9722049 |             53 |   521142272 |               0 |            0 |   7340032 |           NULL | utf8mb4_unicode_520_ci |   
| jan_varchar_index | InnoDB |      10 | Dynamic    | 9738381 |             49 |   486539264 |               0 |    202375168 |   7340032 |           NULL | utf8mb4_unicode_520_ci | 
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

Minha conclusão é que não há diferença de desempenho para este caso de uso específico.

Jan Żankowski
fonte
Sei que agora é tarde, mas ficaria curioso para ver os resultados se você tivesse escolhido uma string menos ideal para a condição where. "califo [rnia]" era ideal, pois podia descartar incompatibilidades após comparar o primeiro caractere, precisando apenas checar mais sobre as correspondências reais; algo como "newham" teria dado resultados mais interessantes, pois seria novo comparar mais caracteres para eliminar todas as incompatibilidades. Além disso, limitar seus números inteiros dessa maneira também acumula chances contra eles, eu teria dado a eles pelo menos 26 valores.
Uueerdo
15
Surpreendentemente, em uma pergunta de 10 anos, essa é apenas uma das duas respostas que não são apenas especulações e se baseiam em referências reais.
Adrian Baker
1
Mas suas tabelas não possuem uma chave primária, que na verdade no InnoDB é uma estrutura de dados classificada. A velocidade entre a classificação inteira e a seqüência de caracteres deve ser diferente.
Melkor
1
@ Melkor Fair point que eu uso em INDEXvez de PRIMARY KEY. Não me lembro do meu raciocínio - provavelmente presumi que PRIMARY KEYé apenas uma INDEXrestrição com exclusividade. No entanto, lendo a seção sobre como as coisas são armazenadas no InnoDB em federico-razzoli.com/primary-key-in-innodb , acho que meus resultados ainda se aplicam às chaves primárias e respondem à pergunta sobre a diferença de desempenho da pesquisa de valor. Além disso, seu comentário sugere analisar o desempenho dos algoritmos de classificação , que não se aplicam ao caso de uso que investigo, que está pesquisando valores em um conjunto.
Jan Żankowski
1
A operação de pesquisa também requer comparações no campo de chave primária (como uma pesquisa binária), em que int deve ser um pouco mais rápido que varchar. Mas, como sugeriram seus experimentos, não é tão óbvio (ou talvez porque você não tenha uma chave primária, portanto, as consultas foram mais lentas). Eu acho que é a mesma coisa na inserção e pesquisa.
Melkor
38

Depende do comprimento. Se o varchar tiver 20 caracteres e o int for 4, se você usar um int, seu índice terá CINCO vezes mais nós por página do espaço do índice no disco ... Isso significa que o deslocamento o índice exigirá um quinto do número de leituras físicas e / ou lógicas.

Portanto, se o desempenho for um problema, dada a oportunidade, sempre use uma chave integral não significativa (chamada substituta) para suas tabelas e para Chaves estrangeiras que fazem referência às linhas nessas tabelas ...

Ao mesmo tempo , para garantir a consistência dos dados, todas as tabelas importantes também devem ter uma chave alternativa não numérica significativa (ou índice exclusivo) para garantir que linhas duplicadas não possam ser inseridas (duplicadas com base em atributos significativos da tabela).

Para o uso específico que você está falando (como pesquisas de estado), isso realmente não importa, porque o tamanho da tabela é muito pequeno. Em geral, não há impacto no desempenho dos índices em tabelas com menos de alguns milhares de linhas. ..

Charles Bretana
fonte
Certo? Não existem mais formatos de dados com base em linhas? Existem outros dados além das chaves. O fator 5 não é utópico?
ManuelSchneid3r
1
@ manuelSchneid3r, o que? utópico? Não, o fator 5 não é "utópico". São apenas 20 divididos por 4. E o que significa "formato de linha com base em dados" significa? Os índices não são "baseados em linha", são estruturas de árvore balanceadas.
Charles Bretana
36

Absolutamente não.

Eu fiz várias ... várias ... verificações de desempenho entre INT, VARCHAR e CHAR.

A tabela de 10 milhões de registros com uma PRIMARY KEY (exclusiva e agrupada) teve exatamente a mesma velocidade e desempenho (e custo de subárvore), independentemente de qual dos três eu usei.

Dito isto ... use o que for melhor para sua aplicação. Não se preocupe com o desempenho.

Timothy Khouri
fonte
42
sentido sem saber quanto tempo os varchars eram ... Se fossem 100 bytes widem então garantido que você não está recebendo o mesmo desempenho que um int 4 bytes
Charles Bretana
6
Também ajudaria a saber qual banco de dados você está usando e qual versão do banco de dados. O ajuste de desempenho quase sempre é trabalhado e aprimorado de versão para versão.
18712 Dave
VARCHAR definitivamente importa para o tamanho do índice. E o índice determina quanto pode caber na memória. E os índices na memória são muito, muito mais rápidos do que aqueles que não são. Pode ser que, para suas 10 milhões de linhas, você tenha 250 MB de memória disponível para esse índice e esteja bem. Mas se você tiver 100m de linhas, ficará menos bem nessa memória.
Paul Draper
9

Para códigos curtos, provavelmente não há diferença. Isso é especialmente verdade, pois a tabela que contém esses códigos provavelmente é muito pequena (no máximo duas mil linhas) e não muda com frequência (quando é a última vez que adicionamos um novo estado dos EUA).

Para tabelas maiores com uma variação maior entre as teclas, isso pode ser perigoso. Pense em usar o endereço de email / nome de usuário de uma tabela Usuário, por exemplo. O que acontece quando você tem alguns milhões de usuários e alguns desses usuários têm nomes longos ou endereços de email. Agora, sempre que você precisar ingressar nesta tabela usando essa chave, ela se tornará muito mais cara.

Joel Coehoorn
fonte
2
Você tem certeza de que isso seria caro? Ou você está apenas adivinhando?
Steve McLeod
Claro que depende da implementação do rdbms, mas pelo que entendi a maioria dos servidores manterá o hash do valor real para fins de indexação. Mesmo assim, e mesmo que seja um hash relativamente curto (digamos, 10 bytes), ainda é mais trabalhoso comparar 2 hashes de 10 bytes do que 2 ints de 4 bytes.
Joel Coehoorn
NUNCA use uma chave longa (ampla) para junções ... Mas se é a melhor representação do que é exclusivo para as linhas da tabela, é melhor que exista uma chave exclusiva (ou índice - que é a mesma coisa) no tabela usando esses valores naturais. As chaves não existem para junções, você pode participar de qualquer coisa que seu coração desejar. As chaves existem para garantir a consistência dos dados.
Charles Bretana
6

Quanto à Chave Primária, o que quer que torne fisicamente uma linha exclusiva deve ser determinado como a chave primária.

Para uma referência como chave estrangeira, o uso de um número inteiro com auto incremento como substituto é uma boa idéia por dois motivos principais.
- Primeiro, geralmente há menos despesas gerais na associação.
- Segundo, se você precisar atualizar a tabela que contém o varchar exclusivo, a atualização deverá cascatear para todas as tabelas filho e atualizar todas elas e também os índices, enquanto que com o substituto int, ele somente precisará atualizar o tabela mestre e seus índices.

A desvantagem de usar o substituto é que você poderia permitir a alteração do significado do substituto:

ex.
id value
1 A
2 B
3 C

Update 3 to D
id value
1 A
2 B
3 D

Update 2 to C
id value
1 A
2 C
3 D

Update 3 to B
id value
1 A
2 C
3 B

Tudo depende do que você realmente precisa se preocupar em sua estrutura e do que significa mais.

LeppyR64
fonte
3

Casos comuns em que um substituto AUTO_INCREMENTdói:

Um padrão de esquema comum é um mapeamento de muitos para muitos :

CREATE TABLE map (
    id ... AUTO_INCREMENT,
    foo_id ...,
    bar_id ...,
    PRIMARY KEY(id),
    UNIQUE(foo_id, bar_id),
    INDEX(bar_id) );

O desempenho desse padrão é muito melhor, especialmente ao usar o InnoDB:

CREATE TABLE map (
    # No surrogate
    foo_id ...,
    bar_id ...,
    PRIMARY KEY(foo_id, bar_id),
    INDEX      (bar_id, foo_id) );

Por quê?

  • As chaves secundárias do InnoDB precisam de uma pesquisa extra; movendo o par para o PK, isso é evitado em uma direção.
  • O índice secundário é "cobertura", portanto, não precisa de pesquisa extra.
  • Essa tabela é menor por causa da eliminação de idum índice.

Outro caso ( país ):

country_id INT ...
-- versus
country_code CHAR(2) CHARACTER SET ascii

Com muita frequência, o iniciante normaliza o código do país em 4 bytes em INTvez de usar uma sequência de caracteres 'natural' de 2 bytes e quase inalterada. Mais rápido, menor, menos JOINs, mais legível.

Rick James
fonte
2

No HauteLook, alteramos muitas de nossas tabelas para usar chaves naturais. Tivemos um aumento no desempenho no mundo real. Como você mencionou, muitas de nossas consultas agora usam menos junções, o que torna as consultas com melhor desempenho. Até usaremos uma chave primária composta, se fizer sentido. Dito isto, algumas tabelas são mais fáceis de trabalhar se tiverem uma chave substituta.

Além disso, se você estiver permitindo que as pessoas gravem interfaces em seu banco de dados, uma chave substituta pode ser útil. A terceira parte pode confiar no fato de que a chave substituta será alterada apenas em circunstâncias muito raras.

Herman J. Radtke III
fonte
2

Eu enfrentei o mesmo dilema. Fiz um DW (esquema de constelação) com 3 tabelas de fatos, acidentes rodoviários, veículos em acidentes e baixas em acidentes. Os dados incluem todos os acidentes registrados no Reino Unido de 1979 a 2012 e 60 tabelas de dimensões. Ao todo, cerca de 20 milhões de registros.

Relacionamentos de tabelas de fatos:

+----------+          +---------+
| Accident |>--------<| Vehicle |
+-----v----+ 1      * +----v----+
     1|                    |1
      |    +----------+    |
      +---<| Casualty |>---+
         * +----------+ *

RDMS: MySQL 5.6

Nativamente, o índice de acidentes é um varchar (números e letras), com 15 dígitos. Tentei não ter chaves substitutas, pois os índices de acidentes nunca mudavam. Em um computador i7 (8 núcleos), o DW ficou muito lento para consultar após 12 milhões de registros de carga, dependendo das dimensões. Depois de muito trabalho e adicionando chaves substitutas bigint, obtive um aumento médio de 20% na velocidade. Ainda com baixo ganho de desempenho, mas tentativa válida. Estou trabalhando no ajuste e cluster do MySQL.

Diego Duarte
fonte
1
Parece que você precisa procurar o particionamento.
jcoffland
2

A questão é sobre o MySQL, então eu digo que há uma diferença significativa. Se fosse sobre o Oracle (que armazena números como string - sim, eu não podia acreditar no começo), então não havia muita diferença.

O armazenamento na tabela não é o problema, mas é a atualização e a referência ao índice. As consultas que envolvem a pesquisa de um registro com base em sua chave primária são frequentes - você deseja que ocorram o mais rápido possível, porque ocorre com muita frequência.

O problema é que uma CPU lida com números inteiros de 4 e 8 bytes naturalmente, em silício . É REALMENTE rápido comparar dois números inteiros - isso acontece em um ou dois ciclos de clock.

Agora observe uma sequência - ela é composta por muitos caracteres (mais de um byte por caractere hoje em dia). A comparação de duas strings para precedência não pode ser feita em um ou dois ciclos. Em vez disso, os caracteres das strings devem ser iterados até que seja encontrada uma diferença. Tenho certeza de que existem truques para torná-lo mais rápido em alguns bancos de dados, mas isso é irrelevante aqui porque uma comparação int é feita naturalmente e extremamente rápida em silício pela CPU.

Minha regra geral - toda chave primária deve ser uma INT autoincrementante, especialmente em aplicativos OO usando um ORM (Hibernate, Datanucleus, qualquer que seja) onde haja muitos relacionamentos entre objetos - eles geralmente sempre são implementados como um FK simples e a capacidade de O banco de dados para resolvê-los rapidamente é importante para a capacidade de resposta do seu aplicativo.

Volksman
fonte
0

Não tenho certeza sobre as implicações de desempenho, mas parece que um possível comprometimento, pelo menos durante o desenvolvimento, seria incluir tanto a chave "substituta" inteira auto-incrementada como a chave pretendida, única e "natural". Isso daria a você a oportunidade de avaliar o desempenho, bem como outros possíveis problemas, incluindo a mutabilidade das chaves naturais.

Dexygen
fonte
0

Como sempre, não há respostas gerais. 'Depende!' e eu não estou sendo ridículo. Meu entendimento da pergunta original era que as chaves em pequenas tabelas - como Country (código inteiro ou código char / varchar) - eram uma chave estrangeira para uma tabela potencialmente enorme, como endereço / tabela de contatos.

Existem dois cenários aqui quando você deseja retornar os dados do banco de dados. Primeiro, é um tipo de consulta de lista / pesquisa em que você deseja listar todos os contatos com códigos ou nomes de estados e países (os IDs não ajudarão e, portanto, precisarão de uma pesquisa). O outro é um cenário de obtenção na chave primária, que mostra um único registro de contato em que o nome do estado, país precisa ser mostrado.

Para o último, provavelmente não importa em que o FK se baseia, pois estamos reunindo tabelas para um único registro ou alguns registros e leituras de teclas. O cenário anterior (pesquisa ou lista) pode ser afetado por nossa escolha. Como é necessário mostrar o país (pelo menos um código reconhecível e talvez até a pesquisa em si inclua um código do país), não é possível ter que ingressar em outra tabela por meio de uma chave substituta (apenas estou sendo cauteloso aqui porque não testei realmente isso, mas parece altamente provável) melhorar o desempenho; apesar do fato de que certamente ajuda na pesquisa.

Como os códigos são pequenos em tamanho - não mais que três caracteres geralmente para país e estado, pode ser bom usar as chaves naturais como chaves estrangeiras nesse cenário.

O outro cenário em que as chaves dependem de valores mais variáveis ​​do varchar e talvez de tabelas maiores; a chave substituta provavelmente tem a vantagem.

Vinod
fonte
0

Permita-me dizer que sim, definitivamente há uma diferença, levando em consideração o escopo do desempenho (definição pronta para uso):

1- O uso de substituto int é mais rápido no aplicativo porque você não precisa usar ToUpper (), ToLower (), ToUpperInvarient () ou ToLowerInvarient () no seu código ou na sua consulta e essas 4 funções têm diferentes parâmetros de desempenho. Veja as regras de desempenho da Microsoft sobre isso. (desempenho da aplicação)

2- O uso de substituto int garante não alterar a chave ao longo do tempo. Até os códigos dos países podem mudar, consulte a Wikipedia como os códigos ISO foram alterados ao longo do tempo. Isso levaria muito tempo para alterar a chave primária das subárvores. (desempenho da manutenção de dados)

3- Parece que existem problemas com as soluções ORM, como o NHibernate quando PK / FK não é int. (desempenho do desenvolvedor)

Shadi Namrouti
fonte