sql >> Database >  >> RDS >> Sqlserver

Omzeiling van SQL Server maximale kolommen limiet 1024 en 8kb recordgrootte

Dit kan gewoon niet. Zie Inside the Storage Engine:anatomie van een record

Ervan uitgaande dat uw tafel ongeveer zo is.

CREATE TABLE T1(
    col_1 varchar(8000) NULL,
    col_2 varchar(8000) NULL,
    /*....*/
    col_999 varchar(8000) NULL,
    col_1000 varchar(8000) NULL
) 

Dan zelfs een rij met allemaal NULL waarden gebruiken de volgende opslag.

  • 1 byte statusbits A
  • 1 byte statusbits B
  • Verschuiving kolomtelling van 2 bytes
  • 125 bytes NULL_BITMAP (1 bit per kolom voor 1.000 kolommen)

Dus dat is een gegarandeerde 129 bytes die al opgebruikt zijn (waardoor er 7.931 overblijft).

Als een van de kolommen een waarde heeft die niet NULL . is of een lege string dan heb je ook ruimte nodig voor

  • 2 bytes variabele lengte kolomtelling (waardoor er 7.929 overblijven).
  • Ergens tussen 2 - 2000 bytes voor de kolom-offset-array.
  • De gegevens zelf.

De kolom-offset-array verbruikt 2 bytes per kolom met variabele lengte behalve als die kolom en alle latere kolommen ook een lengte van nul hebben. Dus update col_1000 zou dwingen om de volledige 2000 bytes te gebruiken tijdens het bijwerken van col_1 zou gewoon 2 bytes gebruiken.

Dus je zou elke kolom kunnen vullen met 5 bytes aan gegevens en als je rekening houdt met de 2 bytes elk in de kolom-offset-array, zou dat optellen tot 7.000 bytes, wat binnen de resterende 7.929 ligt.

De gegevens die u opslaat zijn echter 102 bytes (51 nvarchar tekens) zodat dit buiten de rij kan worden opgeslagen met een 24-byte-aanwijzer naar de feitelijke gegevens die nog in de rij staan.

FLOOR(7929/(24 + 2)) = 304

Dus het beste geval zou zijn dat u 304 kolommen van deze lengtegegevens zou kunnen opslaan en dat is als u bijwerkt vanaf col_1 , col_2 , ... . Als col_1000 gegevens bevat, dan is de berekening

FLOOR(5929/24) = 247

Voor NTEXT de berekening is vergelijkbaar, behalve het kan een 16 byte pointer gebruiken waarmee u gegevens in een paar extra kolommen kunt persen

FLOOR(7929/(16 + 2)) = 440

De noodzaak om al deze off-row-aanwijzingen te volgen voor elke SELECT tegen de tafel zou waarschijnlijk zeer nadelig zijn voor de prestaties.

Script om dit te 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)


  1. Waarschuwing:mysql_result() verwacht dat parameter 1 resource is, boolean gegeven

  2. SQLite JSON_REPLACE()

  3. SELECT gebruikers uit MySQL-database door privileges bitmask?

  4. 'pip install MySQL-python' mislukt met 'IndexError'