sql >> Base de Datos >  >> RDS >> Sqlserver

Evite el límite máximo de columnas de SQL Server de 1024 y un tamaño de registro de 8 kb

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