Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Per aggirare il limite massimo delle colonne di SQL Server, la dimensione del record di 1024 e 8 kb

Questo semplicemente non è possibile. Vedi All'interno del Motore di archiviazione:anatomia di un record

Supponendo che il tuo tavolo sia qualcosa del genere.

CREATE TABLE T1(
    col_1 varchar(8000) NULL,
    col_2 varchar(8000) NULL,
    /*....*/
    col_999 varchar(8000) NULL,
    col_1000 varchar(8000) NULL
) 

Poi anche una riga con tutto NULL i valori utilizzeranno lo spazio di archiviazione seguente.

  • Bit di stato a 1 byte A
  • Bit di stato a 1 byte B
  • Offset conteggio colonne di 2 byte
  • 125 byte NULL_BITMAP (1 bit per colonna per 1.000 colonne)

Quindi sono già 129 byte garantiti esauriti (lasciando 7.931).

Se una qualsiasi delle colonne ha un valore che non è nemmeno NULL o una stringa vuota, hai bisogno anche di spazio per

  • Conteggio colonne a lunghezza variabile 2 byte (lasciando 7.929).
  • Ovunque tra 2 e 2000 byte per l'array di offset delle colonne.
  • I dati stessi.

L'array offset di colonna consuma 2 byte per colonna a lunghezza variabile tranne se anche quella colonna e tutte le colonne successive sono di lunghezza zero. Quindi aggiornando col_1000 forzerebbe l'utilizzo di tutti i 2000 byte durante l'aggiornamento di col_1 userebbe solo 2 byte.

Quindi potresti popolare ogni colonna con 5 byte di dati e, tenendo conto dei 2 byte ciascuno nell'array di offset della colonna, ciò aggiungerebbe fino a 7.000 byte che rientrano nei 7.929 rimanenti.

Tuttavia i dati che stai memorizzando sono 102 byte (51 nvarchar caratteri) in modo che questo possa essere memorizzato fuori riga con un puntatore a 24 byte ai dati effettivi rimanenti nella riga.

FLOOR(7929/(24 + 2)) = 304

Quindi il caso migliore sarebbe che tu possa memorizzare 304 colonne di dati di questa lunghezza e questo è se stai aggiornando da col_1 , col_2 , ... . Se col_1000 contiene dati, quindi il calcolo è

FLOOR(5929/24) = 247

Per NTEXT il calcolo è simile tranne che può utilizzare un puntatore a 16 byte che ti permetterebbe di spremere i dati in alcune colonne extra

FLOOR(7929/(16 + 2)) = 440

La necessità di seguire tutti questi puntatori fuori riga per qualsiasi SELECT contro il tavolo sarebbe probabilmente molto dannoso per le prestazioni.

Script per verificarlo

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)