To po prostu niemożliwe. Zobacz Wewnątrz Silnik pamięci masowej:anatomia rekordu
Zakładając, że Twój stół wygląda mniej więcej tak.
CREATE TABLE T1(
col_1 varchar(8000) NULL,
col_2 varchar(8000) NULL,
/*....*/
col_999 varchar(8000) NULL,
col_1000 varchar(8000) NULL
)
Wtedy nawet wiersz ze wszystkimi NULL
wartości będą używać następującej pamięci.
- 1 bajtowe bity stanu A
- 1 bajtowe bity stanu B
- Przesunięcie liczby kolumn o 2 bajty
- 125 bajtów
NULL_BITMAP
(1bit
za kolumnę dla 1000 kolumn)
To jest gwarantowane 129 bajtów już zużytych (pozostawiając 7931).
Jeśli którakolwiek z kolumn ma wartość inną niż NULL
lub pusty ciąg, potrzebujesz również miejsca na
- Liczba kolumn o zmiennej długości 2 bajty (pozostawiając 7929).
- Wszędzie od 2 do 2000 bajtów dla tablicy przesunięć kolumn.
- Samych danych.
Tablica przesunięcia kolumny zajmuje 2 bajty na kolumnę o zmiennej długości z wyjątkiem jeśli ta kolumna i wszystkie późniejsze kolumny mają również zerową długość. Aktualizuję więc col_1000
wymusiłoby użycie całych 2000 bajtów podczas aktualizowania col_1
użyje tylko 2 bajtów.
Możesz więc wypełnić każdą kolumnę 5 bajtami danych, a biorąc pod uwagę 2 bajty w tablicy przesunięć kolumn, można dodać do 7000 bajtów, co mieści się w pozostałych 7929 bajtach.
Jednak dane, które przechowujesz mają 102 bajty (51 nvarchar
znaków), więc może to być przechowywane poza wierszem z 24-bajtowym wskaźnikiem do rzeczywistych danych pozostałych w wierszu.
FLOOR(7929/(24 + 2)) = 304
Najlepszym przypadkiem byłoby więc przechowywanie 304 kolumn o tej długości danych i to jest, jeśli aktualizujesz z col_1
, col_2
, ...
. Jeśli col_1000
zawiera dane, to obliczenie jest
FLOOR(5929/24) = 247
Dla NTEXT
obliczenia są podobne, z tym wyjątkiem, że może używać 16-bajtowego wskaźnika
co pozwoliłoby na ściśnięcie danych w kilku dodatkowych kolumnach
FLOOR(7929/(16 + 2)) = 440
Konieczność podążania za wszystkimi tymi wskaźnikami poza wierszami dla każdego SELECT
w stosunku do tabeli prawdopodobnie bardzo zaszkodziłoby wydajności.
Skrypt do testowania
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)