varchar (255) o varchar (256)?


21

Dovrei usare varchar(255)o varchar(256)durante la progettazione di tabelle? Ho sentito che un byte viene utilizzato per la lunghezza della colonna o per memorizzare i metadati.

Importa più a questo punto?

Ho visto alcuni post su Internet, tuttavia si applicano a Oracle e MySQL.

Abbiamo Microsoft SQL Server 2016 Enterprise Edition, come si applica a questo ambiente?

Ora dì per esempio, se dicessi ai miei clienti di conservare, ad esempio, una descrizione testuale di 255 caratteri anziché 256, ci sarebbe differenza? Quello che ho letto "Con una lunghezza massima di 255 caratteri, il DBMS può scegliere di utilizzare un singolo byte per indicare la lunghezza dei dati nel campo. Se il limite fosse 256 o maggiore, sarebbero necessari due byte". È vero?


Cordiali saluti: questa domanda è stata inviata nei forum MSDN: social.msdn.microsoft.com/Forums/sqlserver/en-US/…
Solomon Rutzky,

Risposte:


36

Dimensione ogni singola colonna in modo appropriato. NON utilizzare una dimensione "standard" per ogni colonna. Se hai bisogno solo di 30 caratteri, perché creare una colonna in grado di gestire 255? Sono così felice che tu non stia sostenendo di utilizzare varchar(max)per le colonne di stringhe.

Questo è un consiglio particolarmente prudente se hai mai bisogno di indicizzare una colonna o se stai usando una colonna come chiave primaria e ha riferimenti a chiave esterna. SQL Server utilizza le dimensioni di ogni colonna nel suo Query Optimizer per comprendere i requisiti di memoria stimati per l'elaborazione delle query. Avere colonne di grandi dimensioni può essere dannoso per le prestazioni.

Gli indici su colonne di dimensioni eccessive possono causare la generazione di errori:

CREATE TABLE dbo.WideIndex
(
    col1 varchar(255) NOT NULL
    , col2 varchar(255) NOT NULL
    , col3 varchar(600) NOT NULL    
);

CREATE INDEX IX_WideIndex_01
ON dbo.WideIndex (col1, col2, col3);

Il tentativo di creare l'indice sopra risulta in questo avviso:

Avvertimento! La lunghezza massima della chiave è di 900 byte. L'indice 'IX_WideIndex_01' ha una lunghezza massima di 1110 byte. Per alcune combinazioni di valori di grandi dimensioni, l'operazione di inserimento / aggiornamento non riuscirà.

900 byte è la dimensione massima della chiave per gli indici cluster (e gli indici non cluster su SQL Server 2012 e precedenti). 1700 byte è la dimensione massima della chiave per gli indici non cluster nelle versioni più recenti di SQL Server. Se si progettano colonne con una larghezza generica, come (255), è possibile che si verifichi questo avviso molto più spesso del previsto.

Nel caso in cui sia interessato agli interni di archiviazione, è possibile utilizzare il seguente piccolo test per comprendere meglio come SQL Server archivia i dati dell'archivio di righe non compressi.

Innanzitutto, creeremo una tabella in cui possiamo archiviare colonne di varie dimensioni:

IF OBJECT_ID(N'dbo.varchartest', N'U') IS NOT NULL
DROP TABLE dbo.varchartest;
GO

CREATE TABLE dbo.varchartest
(
    varchar30 varchar(30) NOT NULL
    , varchar255 varchar(255) NOT NULL
    , varchar256 varchar(256) NOT NULL
);

Ora inseriremo una singola riga:

INSERT INTO dbo.varchartest (varchar30, varchar255, varchar256)
VALUES (REPLICATE('1', 30), REPLICATE('2', 255), REPLICATE('3', 256));

Questa query utilizza le funzioni non documentate e non supportate sys.fn_RowDumpCrackere sys.fn_PhyslocCrackerper mostrare alcuni dettagli interessanti sulla tabella:

SELECT rdc.*
    , plc.*
FROM dbo.varchartest vct
CROSS APPLY  sys.fn_RowDumpCracker(%%rowdump%%) rdc
CROSS APPLY sys.fn_physlocCracker(%%physloc%%) plc

L'output sarà simile al seguente:

╔═════════════════════╦════════════╦═════════╦════ ══════╦══════════════════════════╦══════════╦═════ ════════╦═════════════╦═════════╦═════════╦═══════ ══╗
║ partition_id ║ colName ║ IsInrow ║ IsSparse ║ IsRecordPrefixCompressed ║ IsSymbol ║ PrefixBytes ║ InRowLength ║ file_id ║ page_id ║ slot_id ║
╠═════════════════════╬════════════╬═════════╬════ ══════╬══════════════════════════╬══════════╬═════ ════════╬═════════════╬═════════╬═════════╬═══════ ══╣
║ 1729382263096344576 ║ varchar30 ║ 1 ║ 0 ║ 0 ║ 0 ║ 0 ║ 30 ║ 1 ║ 1912 ║ 0 ║
║ 1729382263096344576 ║ varchar255 ║ 1 ║ 0 ║ 0 ║ 0 ║ 0 ║ 255 ║ 1 ║ 1912 ║ 0 ║
║ 1729382263096344576 ║ varchar256 ║ 1 ║ 0 ║ 0 ║ 0 ║ 0 ║ 256 ║ 1 ║ 1912 ║ 0 ║
╚═════════════════════╩════════════╩═════════╩════ ══════╩══════════════════════════╩══════════╩═════ ════════╩═════════════╩═════════╩═════════╩═══════ ══╝

Come puoi vedere, InRowLengthviene mostrato per ogni valore, insieme alla posizione di archiviazione fisica di ogni riga: "file_id", "page_id" e "slot_id".

Se prendiamo i valori file_ide page_iddai risultati della query sopra ed DBCC PAGEeseguiamo con essi, possiamo vedere il contenuto effettivo della pagina fisica:

DBCC TRACEON (3604); --send display to the client
DBCC PAGE (tempdb, 1, 1912, 3); --database, file_id, page_id, 3 to show page contents
DBCC TRACEOFF (3604);--reset display back to the error log

I risultati dalla mia macchina sono:

PAGINA: (1: 1912)


BUFFER:


BUF @ 0x00000000FF5B2E80

bpage = 0x0000000024130000 bhash = 0x0000000000000000 bpageno = (1: 1912)
bdbid = 2 breferences = 0 bcputicks = 0
bsampleCount = 0 bUse1 = 32497 bstat = 0x10b
blog = 0x212121cc bnext = 0x0000000000000000          

INTESTAZIONE DI PAGINA:


Pagina @ 0x0000000024130000

m_pageId = (1: 1912) m_headerVersion = 1 m_type = 1
m_typeFlagBits = 0x0 m_level = 0 m_flagBits = 0x8000
m_objId (AllocUnitId.idObj) = 98834 m_indexId (AllocUnitId.idInd) = 7936
Metadati: AllocUnitId = 2233785421652951040                              
Metadati: PartitionId = 1945555045333008384 Metadati: IndexId = 0
Metadati: ObjectId = 34099162 m_prevPage = (0: 0) m_nextPage = (0: 0)
pminlen = 4 m_slotCnt = 1 m_freeCnt = 7538
m_freeData = 652 m_reservedCnt = 0 m_lsn = (35: 210971: 362)
m_xactReserved = 0 m_xdesId = (0: 0) m_ghostRecCnt = 0
m_tornBits = 0 DB Frag ID = 1                      

Stato di allocazione

GAM (1: 2) = SGAM ASSEGNATO (1: 3) = PFS NON ASSEGNATO (1: 1) = 0x41 ALLOCATO 50_PCT_FULL
DIFF (1: 6) = NON MODIFICATO ML (1: 7) = NON MIN_LOGGED           

Slot 0 Offset 0x60 Lunghezza 556

Tipo di record = PRIMARY_RECORD Attributi del record = NULL_BITMAP VARIABLE_COLUMNS
Dimensione record = 556                   
Memory Dump @ 0x000000005145A060

0000000000000000: 30000400 03000003 002d002c 012c0231 31313131 0 ........-.,.,. 11111
0000000000000014: 31313131 31313131 31313131 31313131 31313131 11111111111111111111
0000000000000028: 31313131 31323232 32323232 32323232 32323232 1111122222222222222222
000000000000003C: 32323232 32323232 32323232 32323232 32323232 2222222222222222222222
0000000000000050: 32323232 32323232 32323232 32323232 32323232 2222222222222222222222
0000000000000064: 32323232 32323232 32323232 32323232 32323232 2222222222222222222222
0000000000000078: 32323232 32323232 32323232 32323232 32323232 2222222222222222222222
000000000000008C: 32323232 32323232 32323232 32323232 32323232 2222222222222222222222
00000000000000A0: 32323232 32323232 32323232 32323232 32323232 2222222222222222222222
00000000000000B4: 32323232 32323232 32323232 32323232 32323232 2222222222222222222222
00000000000000C8: 32323232 32323232 32323232 32323232 32323232 2222222222222222222222
00000000000000DC: 32323232 32323232 32323232 32323232 32323232 2222222222222222222222
00000000000000F0: 32323232 32323232 32323232 32323232 32323232 2222222222222222222222
0000000000000104: 32323232 32323232 32323232 32323232 32323232 2222222222222222222222
0000000000000118: 32323232 32323232 32323232 32323232 32323232 2222222222222222222222
000000000000012C: 33333333 33333333 33333333 33333333 33333333 3333333333333333333333
0000000000000140: 33333333 33333333 33333333 33333333 33333333 3333333333333333333333
0000000000000154: 33333333 33333333 33333333 33333333 33333333 3333333333333333333333
0000000000000168: 33333333 33333333 33333333 33333333 33333333 3333333333333333333333
000000000000017C: 33333333 33333333 33333333 33333333 33333333 3333333333333333333333
0000000000000190: 33333333 33333333 33333333 33333333 33333333 3333333333333333333333
00000000000001A4: 33333333 33333333 33333333 33333333 33333333 3333333333333333333333
00000000000001B8: 33333333 33333333 33333333 33333333 33333333 3333333333333333333333
00000000000001CC: 33333333 33333333 33333333 33333333 33333333 3333333333333333333333
00000000000001E0: 33333333 33333333 33333333 33333333 33333333 3333333333333333333333
00000000000001F4: 33333333 33333333 33333333 33333333 33333333 3333333333333333333333
0000000000000208: 33333333 33333333 33333333 33333333 33333333 3333333333333333333333
000000000000021C: 33333333 33333333 33333333 33333333 333333333333333333

Slot 0 Colonna 1 Offset 0xf Lunghezza 30 Lunghezza (fisica) 30

varchar30 = 111111111111111111111111111111                               

Slot 0 Colonna 2 Offset 0x2d Lunghezza 255 Lunghezza (fisica) 255

varchar255 = 222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222
22222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222
222222222222222222222222222222222222222222                               

Slot 0 Colonna 3 Offset 0x12c Lunghezza 256 Lunghezza (fisica) 256

varchar256 = 3333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333
33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333
3333333333333333333333333333333333333333333                              

16

Altri hanno già sottolineato che il numero di byte necessari per memorizzare la lunghezza è fisso. Volevo concentrarmi su questa parte della tua domanda:

Importa più a questo punto?

La tua domanda è taggata con Enterprise Edition, il che significa generalmente che avrai una discreta quantità di dati. Spesso le differenze di un byte per riga in realtà non contano molto nella pratica. Ad esempio, la tabella seguente con una VARCHAR(255)colonna completamente riempita occupa 143176 KB di spazio sul disco:

DROP TABLE IF EXISTS dbo.V255_FULL;

CREATE TABLE dbo.V255_FULL (
    ID1 BIGINT NOT NULL,
    ID2 BIGINT NOT NULL,
    V255 VARCHAR(255)
);

INSERT INTO dbo.V255_FULL WITH (TABLOCK)
SELECT TOP (500000) 0, 0, REPLICATE('A', 255)
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;

EXEC sp_spaceused 'V255_FULL';

risultati:

╔═══════════╦══════════════════════╦═══════════╦═══════════╦════════════╦════════╗
   name             rows          reserved     data     index_size  unused 
╠═══════════╬══════════════════════╬═══════════╬═══════════╬════════════╬════════╣
 V255_FULL  500000                143176 KB  142888 KB  8 KB        280 KB 
╚═══════════╩══════════════════════╩═══════════╩═══════════╩════════════╩════════╝

Creiamo una seconda tabella con una VARCHAR(256)colonna piena . Ci vorrà almeno un altro byte per riga, giusto?

DROP TABLE IF EXISTS dbo.V256_FULL;

CREATE TABLE dbo.V256_FULL (
    ID1 BIGINT NOT NULL,
    ID2 BIGINT NOT NULL,
    V256 VARCHAR(256)
);

INSERT INTO dbo.V256_FULL WITH (TABLOCK)
SELECT TOP (500000) 0, 0, REPLICATE('A', 256)
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;

EXEC sp_spaceused 'V256_FULL';

risultati:

╔═══════════╦══════════════════════╦═══════════╦═══════════╦════════════╦════════╗
   name             rows          reserved     data     index_size  unused 
╠═══════════╬══════════════════════╬═══════════╬═══════════╬════════════╬════════╣
 V256_FULL  500000                143176 KB  142888 KB  8 KB        280 KB 
╚═══════════╩══════════════════════╩═══════════╩═══════════╩════════════╩════════╝

Accade solo che entrambe le tabelle occupino la stessa quantità di spazio. Lo stesso numero di righe si adatta a ciascuna pagina 8k. È fantastico che tu voglia trascorrere del tempo a ottimizzare la tua applicazione, ma sospetto che tu sia meglio concentrarti su aree diverse.


7

La dimensione dichiarata di varchar non ha alcun impatto sulle prestazioni. I dati potrebbero essere effettivamente archiviati come archivio righe con compressione pagine o compressione righe. Come Clustered Columnstore o come tabella ottimizzata per la memoria. Ognuno di questi avrà diversi compromessi prestazionali, ma non importa mai se si dichiara varchar (255) o varchar (256).


9
@ DavidBrowne-Microsoft no, "la dimensione dichiarata di varchar non ha alcun impatto sulle prestazioni" non è assolutamente vera: la dimensione del tipo di dati influisce sulle assegnazioni di memoria per le query. Vedi brentozar.com/archive/2017/02/memory-grants-data-size per maggiori dettagli.
Brent Ozar,

6
Cercare di mantenerlo semplice e scoraggiare l'ottimizzazione prematura.
David Browne - Microsoft
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.