I am creating a table with 1000 columns. Most of the columns are nvarchar type. Table is created, but with a warning
Warning: The t
This simply isn't possible. See Inside the Storage Engine: Anatomy of a record
Assuming your table is something like this.
CREATE TABLE T1(
col_1 varchar(8000) NULL,
col_2 varchar(8000) NULL,
/*....*/
col_999 varchar(8000) NULL,
col_1000 varchar(8000) NULL
)
Then even a row with all NULL values will use the following storage.
NULL_BITMAP (1 bit per column for 1,000 columns)So that is a guaranteed 129 bytes used up already (leaving 7,931).
If any of the columns have a value that is not either NULL or an empty string then you also need space for
The column offset array consumes 2 bytes per variable length column except if that column and all later columns are also zero length. So updating col_1000 would force the entire 2000 bytes to be used whereas updating col_1 would just use 2 bytes.
So you could populate each column with 5 bytes of data and when taking into account the 2 bytes each in the column offset array that would add up to 7,000 bytes which is within the 7,929 remaining.
However the data you are storing is 102 bytes (51 nvarchar characters) so this can be stored off row with a 24 byte pointer to the actual data remaining in row.
FLOOR(7929/(24 + 2)) = 304
So the best case would be that you could store 304 columns of this length data and that is if you are updating from col_1, col_2, .... If col_1000 contains data then the calculation is
FLOOR(5929/24) = 247
For NTEXT the calculation is similar except it can use a 16 byte pointer which would allow you to squeeze data into a few extra columns
FLOOR(7929/(16 + 2)) = 440
The need to follow all these off row pointers for any SELECT against the table would likely be highly detrimental to performance.
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)