Devo adicionar um campo de incremento automático / IDENTITY a uma tabela de referência cruzada apenas para fins de PK?

9

Estou adicionando a seguinte tabela de referência cruzada ao meu banco de dados hospedado no SQL Server:

company_id bigint not null (FK)
org_path nvarchar (2048) not null

O company_idcampo refere-se ao idcampo em outra tabela (na qual é a chave primária).

Dado que também pode haver vários registros com o mesmo company_id, qualquer chave primária teria que usar os dois campos. No entanto, não consigo criar uma chave usando os dois campos porque org_pathé muito longo para o SQL Server.

Quanto a org_path, esta é a única tabela em que existe. É provável que as consultas a esta tabela solicitem todas as entradas ou todas as org_pathentradas por company_id. Ou, dito de outra maneira, parece duvidoso que essa tabela seja consultada org_path. Além disso, é improvável que org_pathseja atualizado e provavelmente inserido e - provavelmente raramente - excluído.

Espero que o número total de linhas esteja na casa dos milhares.

Além disso, o motivo nvarchar (2048)é que o valor deve ser imitado em um banco de dados de terceiros. Um exemplo típico será algo como

\Translation Providers\[customer name]\[order name]\

e pode conter diacríticos.

Portanto, minha pergunta é a seguinte: seria mais eficiente adicionar um idcampo de incremento automático e usá-lo em conjunto com company_ida chave primária ou adicionar sobrecarga desnecessária - e o fato de company_idser a chave primária em outra tabela tem alguma efeito aqui?

awj
fonte

Respostas:

7

Para um índice em cluster não exclusivo comany_idsozinho, o SQL Server adicionará automaticamente um exclusivo de número inteiro de 4 bytes a todas as chaves de índice em cluster duplicadas (isto é, segundo e subsequente para um valor de chave) para torná-lo exclusivo. Isso não é exposto ao usuário.

A vantagem de adicionar seu próprio identificador exclusivo como uma coluna de chave secundária é que você ainda pode procurar por, company_idmas também procurar linhas individuais com mais eficiência (usando em company_id, identitycolvez de company_idcom um predicado residual ativado org_path). O índice de cluster seria então único company_id, identitycol, portanto, nenhum identificador oculto seria adicionado.

Além disso, se você terminar com duplicatas para (company_id,org_path), ter a coluna de identidade explícita (uma espécie de "identificador exclusivo exposto") facilitará o direcionamento de apenas um deles para exclusão ou atualização.

Martin Smith
fonte
12

Uma coisa a considerar é que uma chave primária e um índice de cluster não são a mesma coisa. Uma chave primária é uma restrição e lida com as regras pelas quais os dados permanecem (isto é, integridade dos dados); não tem nada a ver com eficiência / desempenho. Uma Chave Primária exige que a (s) coluna (s) da chave seja (unida) e NÃO NULA (individualmente). Uma PK é aplicada por meio de um índice exclusivo, embora possa ser agrupada ou não agrupada.

Um índice clusterizado é um meio de ordenar fisicamente (ou seja, em disco) os dados na tabela e lidar com o desempenho; não tem nada a ver com integridade de dados. Um índice agrupado podeexige que a (s) coluna (s) chave (s) seja (s) combinada (s), mas não precisa. No entanto, como o Índice de Cluster é a ordem física dos dados, ele precisa identificar cada linha exclusivamente, não importa o quê. Portanto, se você não configurá-lo para exigir exclusividade, ele criará sua própria exclusividade por meio de uma coluna "uniquificador" oculta de 4 bytes. Essa coluna está sempre presente em índices agrupados não exclusivos, mas não ocupa espaço quando os campos-chave são exclusivos (em combinação). Para ver em primeira mão como essa coluna "uniquifier" funciona (tanto no Índice de cluster quanto no efeito de índices não de cluster), consulte este script de teste que publiquei no script PasteBin: T-SQL para testar o tamanho do uniquificador .

Portanto, a principal questão de:

seria mais eficiente adicionar um idcampo de incremento automático e usá-lo em conjunto com company_ida chave primária ou adicionar sobrecarga desnecessária

está confundindo esses dois conceitos, então eles precisam ser abordados separadamente, embora haja definitivamente alguma sobreposição.

Uma IDENTITYcoluna deve ser adicionada ou seria uma sobrecarga desnecessária?

Se você adicionar uma INT IDENTITYcoluna e usá-la para criar uma PK, assumindo que seria uma PK em cluster, que adiciona 4 bytes a cada linha. Esta coluna é visível e utilizável em consultas. Ele pode ser adicionado a outras tabelas como uma chave estrangeira, embora neste caso particular que não vai acontecer.

Se você não adicionar a INT IDENTITYcoluna, não poderá criar uma PK nesta tabela. No entanto, você ainda pode criar um Índice de Cluster na tabela, desde que não use a UNIQUEopção Nesse caso, o SQL Server adicionará uma coluna oculta chamada "uniquifier" que se comporta conforme descrito acima. Como a coluna está oculta, ela não pode ser usada em consultas ou como referência para chaves estrangeiras.

No que diz respeito à eficiência, essas opções são praticamente as mesmas. Sim, haverá um pouco menos de espaço ocupado com o Índice de Cluster não exclusivo devido a algumas linhas (aquelas com os valores de chave exclusivos iniciais) IDENTITYocuparem 0 bytes, enquanto todas as linhas no / PK ocuparão os 4 bytes. Mas não haverá o suficiente das linhas de 0 byte (especialmente com a pequena quantidade esperada) para notar uma diferença, e muito menos pesar a conveniência de poder usar a IDcoluna nas consultas.

Coluna IDENTIDADE INT ou hash da org_pathcoluna computada persistente?

Dado que você não procurará linhas com base em org_pathvalores, não faz sentido adicionar a sobrecarga da Coluna Computada Persistida, além da necessidade de calcular esse hash nas consultas, a fim de corresponder à Coluna Computada (essa era minha sugestão original, disponível no histórico de revisões aqui , que foi baseado na redação / detalhes iniciais da questão). Nesse caso específico, a INT IDENTITYcoluna "ID" provavelmente é a melhor.

Ordem da coluna-chave

Dado que a IDcoluna raramente será usada em consultas, e como os dois principais casos de uso são "todas as linhas" ou "todas as linhas de um determinado company_id", eu criaria o PK company_id, id. E como isso significa que as linhas não são inseridas seqüencialmente, eu especificaria um FILLFACTORde 90. Você também precisará fazer a manutenção regular do índice para reduzir a fragmentação.

Segunda questão

O fato de company_id ser a chave primária em outra tabela tem algum efeito aqui

Não.

Desencadear

Como os org_pathvalores em a company_idsão únicos, você ainda deve criar um acionador INSERT, UPDATEpara aplicar isso. No gatilho, faça um IF EXISTScom uma consulta que provavelmente faz um COUNT(*)e GROUP BY company_id, org_path. Se algo for encontrado, emita a ROLLBACKpara cancelar a operação DML e, em seguida, RAISERRORdizendo que há duplicatas.

Agrupamento

Na minha resposta inicial (com base no texto original / detalhes esparsos da pergunta e disponível no histórico de revisões aqui ), sugeri que fosse possível usar um agrupamento binário (ou seja _BIN2). Agora que temos uma ideia do que exatamente org_pathé, eu não recomendaria o uso de um agrupamento binário. Uma vez que haverá sinais diacríticos, você não quer fazer uso de equivalências linguísticas.

Solomon Rutzky
fonte
Vamos continuar esta discussão no chat .
Solomon Rutzky
0

Por que você precisa de um PK?

Por que não usar o company_id como um índice não clusterizado?

Você disse que a maioria das pesquisas está em todas as entradas ou por company_id
Rarely update
Rarely delete
org_path, esta é a única tabela em que ela existe

A resposta de Martin Smith pode lhe
fornecer o que você precisa. Não estou familiarizado com adicionar automaticamente um exclusivo de número inteiro de 4 bytes.
Talvez esteja faltando alguma coisa, mas se você não tiver outras colunas indexadas, não vejo sentido nesse caso de uso.

Se você está preocupado com DRI, as tabelas devem usar a tabela Empresa como o FK para company_id

paparazzo
fonte
Ei. Em relação a " Por que não apenas ir com company_id como um índice não agrupado? ": Porque isso teria duas desvantagens: 1) seria mais uma coisa ocupando espaço, enquanto um Índice Agrupado é a tabela, portanto, nenhum item adicional e 2) ainda seria necessário uma pesquisa de RID para obter o campo NVARCHAR, a menos que fosse uma INCLUDEcoluna, mas isso é ainda pior, pois está apenas duplicando a tabela. É verdade que o PK não é necessário; a parte importante é o índice de cluster. Mas uma vez que você tenha a IDENTIDADE, é melhor optar pelo PK. E, por favor consulte o novo link na minha resposta para um passeio-through em Uniquifier 😃
Solomon Rutzky
@srutzky Mas ele evita um inteiro uniqueifier 4 byte então eu vejo isso como uma lavagem
paparazzo
Com menos de 10 mil linhas, não importa; você provavelmente precisa estar na casa das milhões de linhas antes de notar o efeito de apenas 4 bytes. Portanto, para a consulta "obter todas as linhas", não há realmente nenhuma diferença em nenhuma dessas opções. Mas para a consulta "get for company_id = @param", ter os dados ordenados fisicamente por company_id ajudará, especialmente quando não for necessário fazer uma pesquisa de RID para cada linha.
Solomon Rutzky
@srutzky Wash é uma lavagem - 10K ou 1G. É apenas algo a considerar pelo OP.
paparazzo