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
(1bit
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)