варчар (255) або варчар (256)?


21

Чи слід використовувати varchar(255)або varchar(256)при проектуванні таблиць? Я чув, що один байт використовується для довжини стовпця або для зберігання метаданих.

Чи важливо це вже в цей момент?

Я бачив деякі повідомлення в Інтернеті, однак вони стосуються Oracle та MySQL.

У нас є Microsoft SQL Server 2016 Enterprise Edition, як це стосується цього середовища?

Тепер скажіть, наприклад, що якщо я наказав своїм клієнтам зберігати, наприклад, опис тексту на 255 символів замість 256, чи є різниця? Що я читав "Максимальна довжина 255 символів, СУБД може вибрати один байт, щоб вказати довжину даних у полі. Якщо обмеження було 256 або більше, знадобляться два байти." Це правда?


FYI: це питання було розміщено на форумах 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 ║ IsRecordPrefixCompression ║ IsSymbol ║ ПрефіксBytes ║ 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 ║
╚═════════════════════╩════════════╩═════════╩════ ══════╩══════════════════════════╩══════════╩═════ ════════╩═════════════╩═════════╩═════════╩═══════ ══╝

Як бачимо, 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)


БУФЕР:


BUF @ 0x00000000FF5B2E80

bpage = 0x0000000024130000 bhash = 0x0000000000000000 bpageno = (1: 1912)
bdbid = 2 значень = 0 bcputicks = 0
bsampleCount = 0 bUse1 = 32497 bstat = 0x10b
blog = 0x212121cc bnext = 0x0000000000000000          

СТОРІНКА КОРИСТУВАЧА:


Сторінка @ 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 Ідентифікатор фрагмента БД = 1                      

Статус розподілу

GAM (1: 2) = ALLOCATED SGAM (1: 3) = NOT ALLOCATED PFS (1: 1) = 0x41 ALLOCATED 50_PCT_FULL
DIFF (1: 6) = НЕ ЗМЕНЕНО ML (1: 7) = НЕ МІНЬОГОЛОГІЧЕНО           

Слот 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 1111111111111111111111
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 333333333333333333

Слот 0 Стовпчик 1 Зсув 0xf Довжина 30 Довжина (фізична) 30

varchar30 = 11111111111111111111111111111111                               

Слот 0 Колонка 2 Зсув 0x2d Довжина 255 Довжина (фізична) 255

varchar255 = 22222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222
2222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222
22222222222222222222222222222222222222222222                               

Слот 0 Стовпчик 3 Зсув 0х12с Довжина 256 Довжина (фізична) 256

varchar256 = 3333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333
3333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333
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 
╚═══════════╩══════════════════════╩═══════════╩═══════════╩════════════╩════════╝

Так буває, що обидві таблиці займають однакову кількість місця. Однакова кількість рядків вміщується на кожній сторінці 8k. Чудово, що ви хочете витратити час на оптимізацію програми, але я підозрюю, що вам краще зосередитися на різних сферах.


7

Заявлений розмір варшара не впливає на продуктивність. Дані можуть бути фактично збережені у вигляді сховища рядків із стисненням сторінки або стисненням рядків. Як кластерний стовпчик стовпців або як таблиця, оптимізована пам'яттю. Кожен з них матиме різні компроміси на продуктивність, але ніколи не має значення, заявляєте ви про варчар (255) або варчар (256).


9
@ DavidBrowne-Microsoft ні, "оголошений розмір варшара не впливає на продуктивність", безумовно, не відповідає дійсності - розмір типу даних впливає на грамати пам'яті для запитів. Докладніші відомості див. У brentozar.com/archive/2017/02/memory-grants-data-size .
Брент Озар

6
Намагаючись зробити це просто, і стримуйте передчасну оптимізацію.
Девід Браун - Microsoft
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.