Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Problemumgehung für die maximalen Spalten von SQL Server mit einer Datensatzgröße von 1024 und 8 KB

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