varchar (255) oder varchar (256)?

21

Soll ich varchar(255)oder varchar(256)beim Entwerfen von Tabellen verwenden? Ich habe gehört, dass ein Byte für die Länge der Spalte oder zum Speichern von Metadaten verwendet wird.

Ist es jetzt noch wichtig?

Ich habe einige Posts im Internet gesehen, die jedoch für Oracle und MySQL gelten.

Wir haben Microsoft SQL Server 2016 Enterprise Edition. Wie trifft dies auf diese Umgebung zu?

Sagen Sie jetzt zum Beispiel, was ist, wenn ich meinen Kunden gesagt habe, dass sie zum Beispiel eine Textbeschreibung mit 255 Zeichen anstelle von 256 beibehalten sollen? Gibt es einen Unterschied? Was ich gelesen habe "Mit einer maximalen Länge von 255 Zeichen kann das DBMS ein einzelnes Byte verwenden, um die Länge der Daten in dem Feld anzugeben. Wenn das Limit 256 oder mehr wäre, wären zwei Bytes erforderlich." Ist das wahr?


quelle
Zu Ihrer Information
Solomon Rutzky,

Antworten:

36

Größe jeder Spalte entsprechend. Verwenden Sie KEINE "Standardgröße" für jede Spalte. Wenn Sie nur 30 Zeichen benötigen, warum erstellen Sie eine Spalte, die 255 Zeichen verarbeiten kann? Ich bin so froh, dass Sie nicht befürworten, varchar(max)für Ihre Zeichenfolgenspalten zu verwenden.

Dies ist besonders ratsam, wenn Sie jemals eine Spalte indizieren müssen oder wenn Sie eine Spalte als Primärschlüssel verwenden und sie Fremdschlüsselreferenzen enthält. SQL Server verwendet die Größe jeder Spalte im Abfrageoptimierer, um den geschätzten Speicherbedarf für die Abfrageverarbeitung zu ermitteln. Übergroße Säulen können die Leistung beeinträchtigen.

Indizes für übergroße Spalten können zu Fehlern führen:

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);

Der Versuch, den obigen Index zu erstellen, führt zu dieser Warnung:

Warnung! Die maximale Schlüssellänge beträgt 900 Byte. Der Index 'IX_WideIndex_01' hat eine maximale Länge von 1110 Bytes. Bei einer Kombination großer Werte schlägt der Einfüge- / Aktualisierungsvorgang fehl.

900 Byte ist die maximale Schlüsselgröße für Clustered-Indizes (und Nicht-Clustered-Indizes in SQL Server 2012 und älteren Versionen). 1700 Byte ist die maximale Schlüsselgröße für nicht gruppierte Indizes in neueren Versionen von SQL Server. Wenn Sie Spalten mit einer generischen Breite wie (255) entwerfen, wird diese Warnung möglicherweise weitaus häufiger als erwartet angezeigt.

Wenn Sie an Speicherinternalen interessiert sind, können Sie den folgenden kleinen Test verwenden, um besser zu verstehen, wie SQL Server nicht komprimierte Zeilenspeicherdaten speichert.

Zuerst erstellen wir eine Tabelle, in der wir Spalten unterschiedlicher Größe speichern können:

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
);

Jetzt fügen wir eine einzelne Zeile ein:

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

Diese Abfrage verwendet die undokumentierten und nicht unterstützten Funktionen sys.fn_RowDumpCrackerund sys.fn_PhyslocCrackerzeigt einige interessante Details zur Tabelle an:

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

Die Ausgabe sieht ungefähr so ​​aus:

╔═════════════════════╦════════════╦═════════╦════ ══════╦══════════════════════════╦══════════╦═════ ════════╦═════════════╦═════════╦═════════╦═══════ ══╗
Partitions_id colName IsInrow IsSparse IsRecordPrefixCompressed IsSymbol PrefixBytes InRowLength file_id page_id slot_id
╠═════════════════════╬════════════╬═════════╬════ ══════╬══════════════════════════╬══════════╬═════ ════════╬═════════════╬═════════╬═════════╬═══════ ══╣
║ 1729382263096344576 30 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
╚═════════════════════╩════════════╩═════════╩════ ══════╩══════════════════════════╩══════════╩═════ ════════╩═════════════╩═════════╩═════════╩═══════ ══╝

Wie Sie sehen, wird der InRowLengthWert für jeden Wert zusammen mit dem physischen Speicherort jeder Zeile angezeigt - "file_id", "page_id" und "slot_id".

Wenn wir die Werte file_idund page_idaus den obigen Abfrageergebnissen nehmen und DBCC PAGEmit ihnen ausführen , können wir den tatsächlichen physischen Seiteninhalt sehen:

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

Die Ergebnisse meiner Maschine sind:

SEITE: (1: 1912)


PUFFER:


BUF @ 0x00000000FF5B2E80

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

KOPFZEILE:


Seite @ 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
Metadaten: AllocUnitId = 2233785421652951040                              
Metadaten: PartitionId = 1945555045333008384 Metadaten: IndexId = 0
Metadaten: 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                      

Zuordnungsstatus

GAM (1: 2) = ALLOCATED SGAM (1: 3) = NOT ALLOCATED PFS (1: 1) = 0x41 ALLOCATED 50_PCT_FULL
DIFF (1: 6) = NICHT GEÄNDERT ML (1: 7) = NICHT MIN_LOGGED           

Slot 0 Offset 0x60 Länge 556

Datensatztyp = PRIMARY_RECORD Datensatzattribute = NULL_BITMAP VARIABLE_COLUMNS
Datensatzgröße = 556                   
Speicherauszug @ 0x000000005145A060

0000000000000000: 30000400 03000003 002d002c 012c0231 31313131 0 ........... 11111
0000000000000014: 31313131 31313131 31313131 31313131 31313131 111111111111111111
0000000000000028: 31313131 31323232 3232323232 32323232 11111222222222222222
000000000000003C: 32323232 3232323232323232323232323232222222222222222222
0000000000000050: 32323232 3232323232323232323232323232222222222222222222
0000000000000064: 32323232 32323232 3232323232 3232323232 22222222222222222222
0000000000000078: 32323232 3232323232323232323232323232222222222222222222
000000000000008C: 32323232 3232323232323232323232323232222222222222222222
00000000000000A0: 32323232 3232323232323232323232323232222222222222222222
00000000000000B4: 32323232 32323232323232323232323232322222222222222222
00000000000000C8: 32323232 3232323232323232323232323232222222222222222222
00000000000000DC: 32323232 3232323232323232323232323232222222222222222222
00000000000000F0: 32323232 3232323232323232323232323232222222222222222222
0000000000000104: 32323232 3232323232323232323232323232222222222222222222
0000000000000118: 32323232 3232323232323232323232323232222222222222222222
000000000000012C: 33333333 33333333 3333333333333333333333333333333333333333
0000000000000140: 33333333 33333333 3333333333333333333333333333333333333333
0000000000000154: 33333333 33333333 3333333333333333333333333333333333333333
0000000000000168: 33333333 33333333 3333333333333333333333333333333333333333
000000000000017C: 33333333 33333333 3333333333333333333333333333333333333333
0000000000000190: 33333333 33333333 3333333333333333333333333333333333333333
00000000000001A4: 33333333 33333333 333333333333333333333333333333333333333333
00000000000001B8: 33333333 33333333 333333333333333333333333333333333333333333
00000000000001CC: 33333333 33333333 3333333333333333333333333333333333333333
00000000000001E0: 33333333 33333333 3333333333333333333333333333333333333333
00000000000001F4: 33333333 33333333 3333333333333333333333333333333333333333
0000000000000208: 33333333 33333333 3333333333333333333333333333333333333333
000000000000021C: 33333333 33333333 333333333333333333333333333333

Slot 0 Spalte 1 Offset 0xf Länge 30 Länge (physikalisch) 30

varchar30 = 1111111111111111111111111111                               

Slot 0 Spalte 2 Offset 0x2d Länge 255 Länge (physikalisch) 255

varchar255 = 222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222
222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222
222222222222222222222222222222222222222222                               

Slot 0 Spalte 3 Offset 0x12c Länge 256 Länge (physikalisch) 256

varchar256 = 33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333
33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333
3333333333333333333333333333333333333333333                              
Max Vernon
quelle
16

Andere haben bereits darauf hingewiesen, dass die Anzahl der zum Speichern der Länge erforderlichen Bytes fest ist. Ich wollte mich in Ihrer Frage auf diesen Teil konzentrieren:

Ist es jetzt noch wichtig?

Ihre Frage ist mit Enterprise Edition gekennzeichnet, was im Allgemeinen bedeutet, dass Sie über eine angemessene Datenmenge verfügen. Oft sind Unterschiede von einem Byte pro Zeile in der Praxis nicht allzu wichtig. Die folgende Tabelle mit einer vollständig gefüllten VARCHAR(255)Spalte belegt beispielsweise 143176 KB Speicherplatz auf dem Datenträger:

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';

Ergebnisse:

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

Lassen Sie uns eine zweite Tabelle mit einer vollständig ausgefüllten VARCHAR(256)Spalte erstellen . Das braucht mindestens ein Byte mehr pro Zeile, oder?

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';

Ergebnisse:

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

Es kommt einfach so vor, dass beide Tabellen den gleichen Platz beanspruchen. Die gleiche Anzahl von Zeilen passt auf jede 8-KB-Seite. Es ist toll, dass Sie Zeit damit verbringen möchten, Ihre Anwendung zu optimieren, aber ich vermute, dass Sie sich besser auf verschiedene Bereiche konzentrieren sollten.

Joe Obbish
quelle
7

Die angegebene Größe des Varchar hat keinen Einfluss auf die Leistung. Die Daten können tatsächlich als Zeilenspeicher mit Seitenkomprimierung oder Zeilenkomprimierung gespeichert werden. Als Clustered Columnstore oder als speicheroptimierte Tabelle. Jedes von diesen hat unterschiedliche Leistungskompromisse, aber es spielt keine Rolle, ob Sie einen varchar (255) oder einen varchar (256) deklarieren.

David Browne - Microsoft
quelle
9
@ DavidBrowne-Microsoft nein, "deklarierte Größe des Varchars hat keine Auswirkung auf die Leistung" ist definitiv nicht wahr - die Datentypgröße wirkt sich auf Speicherzuweisungen für Abfragen aus. Weitere Informationen finden Sie unter brentozar.com/archive/2017/02/memory-grants-data-size .
Brent Ozar
6
Versuchen Sie es einfach zu halten und entmutigen Sie vorzeitige Optimierungen.
David Browne - Microsoft