Coluna NVARCHAR como PRIMARY KEY ou UNIQUE


11

Estou desenvolvendo um banco de dados SQL Server 2012 e tenho uma dúvida sobre as colunas nvarchar como chaves primárias.

Eu tenho esta tabela:

CREATE TABLE [dbo].[CODES]
(
    [ID_CODE] [bigint] IDENTITY(1,1) NOT NULL,
    [CODE_LEVEL] [tinyint] NOT NULL,
    [CODE] [nvarchar](20) NOT NULL,
    [FLAG] [tinyint] NOT NULL,
    [IS_TRANSMITTED] [bit] NOT NULL DEFAULT 0,
     CONSTRAINT [PK_CODES] PRIMARY KEY CLUSTERED 
    (
        [CODE_LEVEL] ASC,
        [CODE] ASC
    )
)

Mas agora eu quero usar a [CODE]coluna como chave primária e remover a [ID_CODE]coluna.

Existe algum problema ou penalidade se eu tiver uma NVARCHARcoluna como PRIMARY KEY?

[CODE]O valor da coluna deve ser exclusivo, portanto, pensei em definir uma UNIQUErestrição para essa coluna.

Preciso usar [CODE]como chave primária ou é melhor se eu definir uma UNIQUErestrição na [CODE]coluna?


11
O que há de mais importante em consideração é quantas linhas haverá na sua tabela?
James Z

Esta não é uma resposta em si , mas estou inclinado a pensar que sua CODEcoluna deve ser única, mas não uma Chave Primária. Eu suspeito que ele carrega informações. Se essas informações puderem CODEser alteradas de alguma forma, você deverá mudar ou estar desatualizado. Isso tornaria sua Chave Primária volátil, e não consigo ver isso terminando bem. É melhor deixar seu PK ser apenas uma chave, e seu CÓDIGO pode fazer o que quiser. Apenas uma opinião.
Manngo 18/03/19

@ Manngo, obrigado pelo seu comentário. Sim, eu fiz dessa maneira: ID_CODE é a chave primária e CODE é ÚNICO.
VansFannel

Respostas:


13

Sim, existem consequências negativas para o uso de uma string em vez de um tipo numérico para uma Chave Primária, e ainda mais se o PK estiver em Cluster (o que de fato é o seu caso). No entanto, o grau em que você vê o (s) efeito (s) do uso de um campo de sequência é uma função de a) quantas linhas estão nesta tabela eb) quantas linhas em outras tabelas têm Chave Estrangeira para este PK. Se você tiver apenas 10k linhas nesta tabela e 100k linhas em algumas outras tabelas que FK a esta tabela através desse campo, talvez não seja tão perceptível. Mas esses efeitos certamente se tornam mais visíveis à medida que a contagem de linhas aumenta.

Você precisa considerar que os campos em um Índice de cluster são transferidos para índices não de cluster. Portanto, você não está apenas olhando até 40 bytes por linha, mas (40 * some_number) bytes. E em todas as tabelas FK, você tem os mesmos 40 bytes na linha e, mais frequentemente, haverá um índice Não-Clusterizado nesse campo, pois está sendo usado em JOINs, portanto, agora é realmente duplicado em todas as tabelas que FK este. Se alguém está inclinado a pensar que 40 bytes * 1 milhão de linhas * 10 cópias não são motivo de preocupação, consulte o meu artigo O disco é barato! ORLY? que detalha todas (ou pelo menos a maioria) das áreas afetadas por esta decisão.

A outra coisa a considerar é que filtrar e classificar strings, especialmente quando não estiver usando um agrupamento binário (presumo que você esteja usando o padrão de banco de dados que normalmente não diferencia maiúsculas de minúsculas) é muito menos eficiente (ou seja, leva mais tempo) do que quando usa INT/ BIGINT. Isso afeta todas as consultas que filtram / ingressam / classificam nesse campo.

Portanto, usar algo como CHAR(5)provavelmente seria bom para um PK em cluster, mas principalmente se também fosse definido com COLLATE Latin1_General_100_BIN2(ou algo parecido).

E o valor de [CODE]alguma vez pode mudar? Se sim, é mais um motivo para não usá-lo como PK (mesmo que você defina os FKs ON UPDATE CASCADE). Se não puder ou nunca mudar, tudo bem, mas ainda há motivos mais do que suficientes para não usá-lo como um PK em cluster.

Obviamente, a pergunta pode ser formulada incorretamente, pois parece que você já possui esse campo no seu PK.

Independentemente disso, sua melhor opção, de longe, é usar [ID_CODE]como PK em cluster, usar esse campo em tabelas relacionadas como FK e manter [CODE]como UNIQUE INDEX(o que significa que é uma "chave alternativa").


Atualizar
Um pouco mais de informação com base nesta pergunta em um comentário a esta resposta:

[ID_CODE], como PRIMARY KEY, é a melhor opção se eu usar a coluna [CODE] para pesquisar a tabela?

Tudo isso depende de muitos fatores, alguns dos quais eu já mencionei, mas que reafirmaremos:

Uma Chave Primária é como a linha individual é identificada, independentemente de ser referenciada por Chaves Estrangeiras. Como o sistema identifica internamente a linha está relacionado, mas não necessariamente o mesmo, como seus usuários se identificam / nessa linha. Qualquer coluna NOT NULL com dados exclusivos pode funcionar, mas há questões de praticidade a serem consideradas, especialmente se a PK for, de fato, referenciada por quaisquer FKs. Por exemplo, os GUIDs são únicos e algumas pessoas realmente gostam de usá-los por vários motivos, mas são muito ruins para índices de cluster ( NEWSEQUENTIALIDé melhor, mas não perfeito). Por outro lado, os GUIDs são ótimos como chaves alternativas e são usados ​​pelo aplicativo para procurar a linha, mas os JOINs ainda são feitos usando uma PK INT (ou similar).

Até agora, você não nos disse como o [CODE]campo se encaixa no sistema de todos os ângulos, além de mencionar agora que é assim que você procura linhas, mas isso é para todas as consultas ou apenas algumas? Conseqüentemente:

  • Em relação ao [CODE]valor:

    • Como é gerado?
    • É incremental ou psuedo-aleatório?
    • É comprimento uniforme ou comprimento variável?
    • Quais caracteres são usados?
    • Se estiver usando caracteres alfabéticos: diferencia maiúsculas de minúsculas ou não faz distinção?
    • Ele pode mudar depois de ser inserido?
  • Em relação a esta tabela:

    • Alguma outra tabela FK nesta tabela? Ou esses campos ( [CODE]ou [ID_CODE]) são usados ​​em outras tabelas, mesmo que não sejam explicitamente com chave estrangeira?
    • Se [CODE] o único campo é usado para obter linhas individuais, qual é a finalidade do [ID_CODE]campo? Se não for usado, por que tê-lo em primeiro lugar (que pode depender da resposta para "O [CODE]campo pode mudar?")?
    • Quantas linhas nesta tabela?
    • Se outras tabelas fizerem referência a esta tabela, quantas e quantas linhas em cada uma delas?
    • Quais são os índices para esta tabela?

Esta decisão não pode ser tomada puramente na questão de "NVARCHAR sim ou não?". Mais uma vez direi que, de um modo geral, não acho que seja uma boa ideia, mas certamente há momentos em que está bem. Dado tão poucos campos nesta tabela, não é provável que exista mais, ou pelo menos não muitos, índices. Portanto, você pode estar bem de qualquer maneira [CODE]como o Índice de Cluster. E se nenhuma outra tabela fizer referência a essa tabela, você também poderá fazer o PK. Mas, se outras tabelas fizerem referência a essa tabela, eu optaria pelo [ID_CODE]campo como PK, mesmo se Não estiver em cluster.


O defensor anônimo anônimo (que parece também ter votado negativamente a resposta de @noIDonthissystem) se importaria em oferecer qualquer crítica construtiva ou apontar alguma lógica falha?
Solomon Rutzky

Obrigado pela sua resposta. É [ID_CODE], como PRIMARY KEY, a melhor opção se eu usar a [CODE]coluna para procurar a tabela?
VansFannel

@VansFannel, veja a minha atualização. obrigado.
Solomon Rutzky

Entrei para a comunidade do dba para votar apenas esta resposta.
Ahmet Arslan

6

Você precisa separar os conceitos:

  • chave primária é um conceito de design , uma propriedade lógica das entradas na tabela. Deve ser imutável durante a vida útil da entrada da tabela e deve ser a chave usada no aplicativo para referenciar a entrada.

  • índice clusterizado é um conceito de armazenamento , uma propriedade física. Deve ser o caminho de acesso mais comum para consultas, deve servir para satisfazer o índice de cobertura na maioria dos casos e atender ao maior número possível de consultas de intervalo.

Não é necessário que a chave primária seja o índice em cluster. Você pode ter ID_CODEcomo PK e (CODE_LEVEL, CODE)como chave em cluster. Ou o contrário.

Uma chave agrupada maior tem algumas repercussões negativas, pois a chave mais ampla significa menor densidade nas páginas de índice e tamanho maior consumido em todos os índices não agrupados. já houve toneladas de tinta derramadas sobre esse tópico, por exemplo. start from Mais considerações sobre a chave de cluster - o debate sobre o índice em cluster continua! .

Mas a questão principal é que a escolha da chave de índice clusterizado é principalmente uma troca. Por um lado, você tem requisitos de tamanho de armazenamento, com repercussões gerais no desempenho (chave maior -> tamanho maior -> mais IO, e a largura de banda de IO é provavelmente o recurso mais escasso que você possui). Por outro lado, escolher a chave em cluster incorreta em nome da economia de espaço pode ter consequências no desempenho da consulta, geralmente piores que os problemas resultantes de uma chave ampla.

Quanto à escolha da chave primária, isso nem deve ser um problema: seu modelo de dados, sua lógica de aplicativo deve ditar qual é a chave primária.

Dito isto, o meu 2c: nãoNVARCHAR(20) é amplo. É um tamanho de chave agrupada perfeitamente aceitável, mesmo para uma tabela grande.


Obrigado pela sua resposta. É [ID_CODE], como PRIMARY KEY, a melhor opção se eu usar a [CODE]coluna (e talvez [CODE_LEVEL]) para procurar a tabela?
VansFannel

Apenas no @VansFannel, você pode responder a isso.
Remus Rusanu

Mas na sua opinião ...
VansFannel

2
Minha opinião teria que considerar o DDL exato da tabela inteira e todos os índices, as chaves estrangeiras que a referenciavam, o número estimado de linhas, a carga de trabalho de consulta esperada, os SLAs esperados do aplicativo e, no mínimo, o disponível disponível para hardware e licenciamento.
Remus Rusanu

Obrigado. Vou usar a [CODE]coluna como chave primária.
VansFannel

4

Eu nunca permitiria que alguém fizesse nvarchar(20)um PK no meu banco de dados. Você perde espaço em disco e cache de memória. Todo índice nessa tabela e todos os FKs replicam esse amplo valor. Talvez um caractere (20), se puderem justificá-lo. Em que tipo de dados você está tentando armazenar CODE? Você realmente precisa armazenar caracteres nvarchar? Costumo fazer com que os valores "internos" das PKs não sejam vistos pelos usuários e tento manter os valores exibidos separadamente. Às vezes, os valores exibidos precisam ser alterados, o que se torna muito problemático com PKs + FKs.

Além disso, você percebe que uma 'identidade bigint (1,1)' pode incrementar até 9.223.372.036.854.775.807?

[ID_CODE] [bigint] IDENTITY(1,1)

A menos que você esteja criando esse banco de dados para o Google, um normal int identity (1,1)com limite de mais de 2 bilhões não será suficiente?


int é de 4 bytes no SQL, o que fornece -2,1 bilhões a + 2,1 bilhões.
datagod

@ datagod, ha obrigado, tantos dígitos que contei errado!
nenhum ID neste sistema

Obrigado pela sua resposta. É [ID_CODE], como PRIMARY KEY, a melhor opção se eu usar a [CODE]coluna para procurar a tabela? Obrigado.
VansFannel

Eu costumava estar neste barco até que alguém usasse a natureza seqüencial de "int" para prever dados / usuários no meu banco de dados e coletei quase tudo o que tinha. Nunca mais. O DB voltado para o público precisa ser um pouco mais difícil de obter informações.
DaBlue

3

Não deve haver nenhuma penalidade inerente / perceptível que não seja o risco de usar teclas largas ao usar nvarchar / varchar, se não estiver ciente. Especialmente se você começar a combiná-los em chaves compostas.

Mas no seu exemplo de (20) comprimento, você deve estar bem e eu não me preocuparia muito com isso. Porque se CODE é como você consulta principalmente seus dados - um índice clusterizado parece muito sensato.

No entanto, você deve considerar se realmente deseja uma chave primária ou apenas um índice exclusivo (em cluster). Há uma (pequena) diferença entre o índice em cluster e a chave primária (basicamente - chave primária identifica seus dados, mas o índice é como você consulta dados); portanto, se desejar, você pode facilmente criar seu ID_Code como uma chave primária e crie um índice clusterizado exclusivo sobre CODE. (aviso: o SQL Server transforma automaticamente sua Chave Primária em um índice em cluster, a menos que você tenha criado manualmente o índice em cluster)

Considere também se você realmente precisa do ID_Code agora que possui um CÓDIGO exclusivo.


2
Na verdade, NVARCHAR(20)tem 40 bytes de tamanho (máximo) e, como é uma coluna de comprimento variável , não é realmente a melhor opção para um índice em cluster. ID_CODEser um BIGINT IDENTITYseria a escolha muito melhor aqui!
10246 Marc

Eu sei que são 40 bytes, mas não havia muitas razões para especificá-lo, já que ele não está nem perto dos 900 bytes. E se você consultar principalmente os dados do CODE, seria uma opção melhor evitar a manutenção de índices redundantes, porque você ainda precisaria de um índice e, em seguida, teria que procurar nos agrupamentos posteriores
Allan S. Hansen

Vale mencionar - que eu esqueci de mencionar e que suspeito que é onde @marc_s está abordando é que um índice desse tipo pode levar a uma fragmentação maior do que uma identidade seqüencial, mas ainda o vejo como um índice sensato nessa situação específica no fator de consulta.
Allan S. Hansen
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.