varchar (255) или varchar (256)?

21

Должен ли я использовать varchar(255)или varchar(256)при разработке таблиц? Я слышал, один байт используется для длины столбца или для хранения метаданных.

Имеет ли это значение в данный момент?

Я видел некоторые посты в интернете, однако они относятся к Oracle и MySQL.

У нас есть Microsoft SQL Server 2016 Enterprise Edition, как это применимо к этой среде?

Скажем, например, что если я скажу своим клиентам, например, оставить текстовое описание 255 символов вместо 256, есть ли разница? Что я прочитал: «При максимальной длине в 255 символов СУБД может выбрать использование одного байта для указания длины данных в поле. Если бы ограничение было 256 или больше, потребовалось бы два байта». Это правда?


источник
К вашему сведению: этот вопрос был опубликован на форумах MSDN: social.msdn.microsoft.com/Forums/sqlserver/en-US/…
Соломон Руцки,

Ответы:

36

Размер каждого столбца соответственно. НЕ используйте «стандартный» размер для каждого столбца. Если вам нужно только 30 символов, зачем создавать столбец, который может обрабатывать 255? Я так рад, что вы не пропагандируете использование varchar(max)для своих строковых столбцов.

Это особенно разумный совет, если вам когда-либо нужно проиндексировать столбец, или если вы используете столбец в качестве первичного ключа, и у него есть ссылки на внешние ключи. SQL Server использует размер каждого столбца в оптимизаторе запросов, чтобы понять предполагаемые требования к памяти для обработки запросов. Наличие негабаритных столбцов может отрицательно сказаться на производительности.

Индексы столбцов, размер которых превышает допустимый, могут привести к возникновению ошибок:

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

Попытка создать индекс выше приводит к этому предупреждению:

Предупреждение! Максимальная длина ключа составляет 900 байтов. Индекс 'IX_WideIndex_01' имеет максимальную длину 1110 байтов. Для некоторой комбинации больших значений операция вставки / обновления завершится неудачно.

900 байтов - это максимальный размер ключа для кластеризованных индексов (и некластеризованных индексов в SQL Server 2012 и более ранних версиях). 1700 байт - это максимальный размер ключа для некластеризованных индексов в новых версиях SQL Server. Если вы проектируете столбцы с общей шириной, такой как (255), вы можете столкнуться с этим предупреждением гораздо чаще, чем ожидалось.

Если вас интересуют внутренние устройства хранения, вы можете использовать следующий небольшой тест, чтобы лучше понять, как SQL Server хранит несжатые данные хранилища строк.

Сначала мы создадим таблицу, в которой мы можем хранить столбцы разных размеров:

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

Теперь мы вставим одну строку:

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

Этот запрос использует недокументированные и неподдерживаемые функции sys.fn_RowDumpCrackerи sys.fn_PhyslocCrackerпоказывает некоторые интересные детали о таблице:

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

Вывод будет выглядеть примерно так:

╔═════════════════════╦════════════╦═════════╦════ ══════╦══════════════════════════╦══════════╦═════ ════════╦═════════════╦═════════╦═════════╦═══════ ══╗
║ partition_id ║ colName ║ IsInrow ║ IsSparse ║ IsRecordPrefixСжатый ║ IsSymbol ║ ПрефиксBytes ║ InRowLength ║ file_id ║ page_id ║ slot_id ║
╠═════════════════════╬════════════╬═════════╬════ ══════╬══════════════════════════╬══════════╬═════ ════════╬═════════════╬═════════╬═════════╬═══════ ══╣
1729382263096344576 76 varchar30 ║ 1 ║ 0 ║ 0 ║ 0 ║ 0 ║ 30 ║ 1 ║ 1912 ║ 0 ║
1729382263096344576 76 varchar255 ║ 1 ║ 0 ║ 0 ║ 0 ║ 0 ║ 255 ║ 1 ║ 1912 ║ 0 ║
29 1729382263096344576 ║ varchar256 ║ 1 ║ 0 ║ 0 ║ 0 ║ 0 ║ 256 ║ 1 ║ 1912 ║ 0 ║
╚═════════════════════╩════════════╩═════════╩════ ══════╩══════════════════════════╩══════════╩═════ ════════╩═════════════╩═════════╩═════════╩═══════ ══╝

Как видите, InRowLengthдля каждого значения отображается вместе с физическим местом хранения каждой строки - «file_id», «page_id» и «slot_id».

Если взять file_idи page_idзначение из результатов запроса выше и работать DBCC PAGEс ними, мы можем видеть фактическое содержание физических страниц:

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

Результаты от моей машины:

СТРАНИЦА: (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          

ЗАГОЛОВОК СТРАНИЦЫ:


Page @ 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
Метаданные: AllocUnitId = 2233785421652951040                              
Метаданные: PartitionId = 1945555045333008384 Метаданные: IndexId = 0
Метаданные: 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 ID фрагмента БД = 1                      

Статус распределения

GAM (1: 2) = ВЫДЕЛЕННАЯ SGAM (1: 3) = НЕ ВЫДЕЛЕННАЯ PFS (1: 1) = 0x41 ALLOCATED 50_PCT_FULL
DIFF (1: 6) = НЕ ИЗМЕНЕНО ML (1: 7) = НЕ MIN_LOGGED           

Слот 0 Смещение 0x60 Длина 556

Тип записи = PRIMARY_RECORD Атрибуты записи = NULL_BITMAP VARIABLE_COLUMNS
Размер записи = 556                   
Дамп памяти @ 0x000000005145A060

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

Слот 0 Колонка 1 Смещение 0xf Длина 30 Длина (физическая) 30

varchar30 = 111111111111111111111111111111                               

Слот 0 Колонка 2 Смещение 0x2d Длина 255 Длина (физическая) 255

varchar255 = 22222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222
22222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222
222222222222222222222222222222222222222222                               

Слот 0 Колонка 3 Смещение 0x12c Длина 256 Длина (физическая) 256

varchar256 = 3333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333
33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333
3333333333333333333333333333333333333333333                              
Макс Вернон
источник
16

Другие уже указывали, что число байтов, необходимое для хранения длины, является фиксированным. Я хотел бы сосредоточиться на этой части в вашем вопросе:

Имеет ли это значение в данный момент?

Ваш вопрос помечен корпоративной версией, что обычно означает, что у вас будет достаточно данных. Часто различия одного байта в строке на практике не имеют большого значения. Например, следующая таблица с полностью заполненным VARCHAR(255)столбцом занимает 143176 КБ места на диске:

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

Результаты:

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

Давайте создадим вторую таблицу с полностью заполненным VARCHAR(256)столбцом. Это займет как минимум еще один байт на строку, верно?

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

Результаты:

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

Так получилось, что обе таблицы занимают одинаковое количество места. Одинаковое количество строк умещается на каждой странице 8 КБ. Это здорово, что вы хотите потратить время на оптимизацию своего приложения, но я подозреваю, что вам лучше сосредоточиться на разных областях.

Джо Оббиш
источник
7

Заявленный размер varchar не влияет на производительность. Данные могут фактически храниться как хранилище строк со сжатием страниц или сжатием строк. В качестве кластерного хранилища столбцов или таблицы, оптимизированной для памяти. Каждый из них будет иметь различные компромиссы в производительности, но не имеет значения, объявляете ли вы varchar (255) или varchar (256).

Дэвид Браун - Microsoft
источник
9
@ DavidBrowne-Microsoft нет, «заявленный размер varchar не влияет на производительность», безусловно, не соответствует действительности - размер типа данных влияет на предоставление памяти для запросов. См. Brentozar.com/archive/2017/02/memory-grants-data-size для получения дополнительной информации.
Брент Озар
6
Попытка сделать это простым и препятствовать преждевременной оптимизации.
Дэвид Браун - Microsoft,