Esto simplemente no es posible. Consulte Dentro del Motor de almacenamiento:anatomía de un registro
Asumiendo que tu mesa es algo como esto.
CREATE TABLE T1(
col_1 varchar(8000) NULL,
col_2 varchar(8000) NULL,
/*....*/
col_999 varchar(8000) NULL,
col_1000 varchar(8000) NULL
)
Luego, incluso una fila con todos los NULL
los valores usarán el siguiente almacenamiento.
- bits de estado de 1 byte A
- bits de estado de 1 byte B
- Compensación de recuento de columnas de 2 bytes
- 125 bytes
NULL_BITMAP
(1bit
por columna para 1000 columnas)
Así que ya se han agotado 129 bytes garantizados (dejando 7931).
Si alguna de las columnas tiene un valor que tampoco es NULL
o una cadena vacía, entonces también necesita espacio para
- Recuento de columnas de longitud variable de 2 bytes (dejando 7929).
- En cualquier lugar entre 2 y 2000 bytes para la matriz de desplazamiento de columna.
- Los datos en sí.
La matriz de desplazamiento de columna consume 2 bytes por columna de longitud variable excepto si esa columna y todas las columnas posteriores también tienen una longitud cero. Así que actualizando col_1000
obligaría a usar los 2000 bytes completos al actualizar col_1
solo usaría 2 bytes.
Por lo tanto, podría completar cada columna con 5 bytes de datos y, al tener en cuenta los 2 bytes de cada uno en la matriz de desplazamiento de columna, sumaría 7000 bytes, que están dentro de los 7929 restantes.
Sin embargo, los datos que está almacenando son 102 bytes (51 nvarchar
caracteres) para que esto se pueda almacenar fuera de la fila con un puntero de 24 bytes a los datos reales que quedan en la fila.
FLOOR(7929/(24 + 2)) = 304
Entonces, el mejor caso sería que pudiera almacenar 304 columnas de esta longitud de datos y eso si está actualizando desde col_1
, col_2
, ...
. Si col_1000
contiene datos, entonces el cálculo es
FLOOR(5929/24) = 247
Para NTEXT
el cálculo es similar excepto que puede usar un puntero de 16 bytes
lo que le permitiría comprimir datos en algunas columnas adicionales
FLOOR(7929/(16 + 2)) = 440
La necesidad de seguir todos estos punteros fuera de fila para cualquier SELECT
contra la mesa probablemente sería muy perjudicial para el rendimiento.
Script para probar esto
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)