Das ist einfach nicht möglich. Siehe Inside the Speicher-Engine:Anatomie eines Datensatzes
Angenommen, Ihre Tabelle sieht in etwa so aus.
CREATE TABLE T1(
col_1 varchar(8000) NULL,
col_2 varchar(8000) NULL,
/*....*/
col_999 varchar(8000) NULL,
col_1000 varchar(8000) NULL
)
Dann noch eine Zeile mit allen NULL
Werte verwenden den folgenden Speicher.
- 1 Byte Statusbits A
- 1 Byte Statusbits B
- Spaltenanzahl-Offset von 2 Byte
- 125 Byte
NULL_BITMAP
(1bit
pro Spalte für 1.000 Spalten)
Das sind also garantiert bereits 129 Bytes verbraucht (verbleiben 7.931).
Wenn eine der Spalten einen Wert hat, der nicht entweder NULL
ist oder ein leerer String, dann brauchen Sie auch Platz für
- Spaltenanzahl mit variabler Länge von 2 Byte (verlässt 7.929).
- Irgendwo zwischen 2 und 2000 Byte für das Spalten-Offset-Array.
- Die Daten selbst.
Das Spalten-Offset-Array verbraucht 2 Bytes pro Spalte mit variabler Länge außer wenn diese Spalte und alle späteren Spalten auch die Länge Null haben. Aktualisieren Sie also col_1000
würde erzwingen, dass die gesamten 2000 Bytes verwendet werden, während col_1
aktualisiert wird würde nur 2 Bytes verwenden.
Sie könnten also jede Spalte mit 5 Bytes Daten füllen, und wenn Sie die 2 Bytes im Spalten-Offset-Array berücksichtigen, würde dies 7.000 Bytes ergeben, was innerhalb der verbleibenden 7.929 liegt.
Die Daten, die Sie speichern, sind jedoch 102 Bytes (51 nvarchar
Zeichen), sodass dies außerhalb der Zeile mit einem 24-Byte-Zeiger auf die tatsächlichen Daten gespeichert werden kann, die in der Zeile verbleiben.
FLOOR(7929/(24 + 2)) = 304
Der beste Fall wäre also, dass Sie 304 Spalten dieser Länge von Daten speichern könnten, und das ist, wenn Sie von col_1
aktualisieren , col_2
, ...
. Wenn col_1000
Daten enthält, dann ist die Berechnung
FLOOR(5929/24) = 247
Für NTEXT
die Berechnung ist ähnlich, außer dass ein 16-Byte-Zeiger verwendet werden kann
wodurch Sie Daten in ein paar zusätzliche Spalten quetschen könnten
FLOOR(7929/(16 + 2)) = 440
Die Notwendigkeit, all diesen Off-Row-Zeigern für jedes SELECT
zu folgen gegen die Tabelle wäre wahrscheinlich sehr nachteilig für die Leistung.
Skript zum Testen
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)