Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Como contornar o limite máximo de colunas do SQL Server de 1024 e 8kb de tamanho de registro


Isso simplesmente não é possível. Veja Dentro do Mecanismo de armazenamento:anatomia de um registro

Supondo que sua tabela seja algo assim.
CREATE TABLE T1(
    col_1 varchar(8000) NULL,
    col_2 varchar(8000) NULL,
    /*....*/
    col_999 varchar(8000) NULL,
    col_1000 varchar(8000) NULL
) 

Em seguida, até mesmo uma linha com todos os NULL valores usarão o seguinte armazenamento.
  • bits de status de 1 byte A
  • bits de status de 1 byte B
  • Deslocamento da contagem de colunas de 2 bytes
  • 125 bytes NULL_BITMAP (1 bit por coluna para 1.000 colunas)

Portanto, são 129 bytes garantidos já usados ​​(deixando 7.931).

Se alguma das colunas tiver um valor que não seja NULL ou uma string vazia, então você também precisa de espaço para
  • Contagem de colunas de comprimento variável de 2 bytes (deixando 7.929).
  • Qualquer lugar entre 2 e 2.000 bytes para a matriz de deslocamento da coluna.
  • Os próprios dados.

A matriz de deslocamento de coluna consome 2 bytes por coluna de comprimento variável exceto se essa coluna e todas as colunas posteriores também tiverem comprimento zero. Atualizando col_1000 forçaria que todos os 2.000 bytes fossem usados ​​ao atualizar col_1 usaria apenas 2 bytes.

Assim, você pode preencher cada coluna com 5 bytes de dados e, ao levar em consideração os 2 bytes de cada na matriz de deslocamento da coluna, isso somaria 7.000 bytes, o que está dentro dos 7.929 restantes.

No entanto, os dados que você está armazenando são 102 bytes (51 nvarchar caracteres) para que isso possa ser armazenado fora da linha com um ponteiro de 24 bytes para os dados reais restantes na linha.
FLOOR(7929/(24 + 2)) = 304

Portanto, o melhor caso seria armazenar 304 colunas com esses dados de comprimento e isso se você estiver atualizando de col_1 , col_2 , ... . Se col_1000 contém dados, então o cálculo é
FLOOR(5929/24) = 247

Para NTEXT o cálculo é semelhante, exceto que pode usar um ponteiro de 16 bytes o que permitiria que você comprimisse dados em algumas colunas extras
FLOOR(7929/(16 + 2)) = 440

A necessidade de seguir todos esses ponteiros de linha para qualquer SELECT contra a mesa provavelmente seria altamente prejudicial ao desempenho.

Script para testar isso

DROP TABLE T1

/* Create table with 1000 columns*/
DECLARE @CreateTableScript nvarchar(max) = 'CREATE TABLE T1('

SELECT @CreateTableScript += 'col_' + LTRIM(number) + ' VARCHAR(8000),'
FROM master..spt_values
WHERE type='P' AND number BETWEEN 1 AND 1000
ORDER BY number

SELECT @CreateTableScript += ')'

EXEC(@CreateTableScript)

/* Insert single row with all NULL*/
INSERT INTO T1 DEFAULT VALUES


/*Updating first 304 cols succeed. Change to 305 and it fails*/
DECLARE @UpdateTableScript nvarchar(max) = 'UPDATE T1 SET  '

SELECT @UpdateTableScript += 'col_' + LTRIM(number) + ' = REPLICATE(1,1000),'
FROM master..spt_values
WHERE type='P' AND number BETWEEN 1 AND 304
ORDER BY number

SET @UpdateTableScript = LEFT(@UpdateTableScript,LEN(@UpdateTableScript)-1)
EXEC(@UpdateTableScript)