varchar(255)还是varchar(256)?


21

我应该使用表格varchar(255)还是varchar(256)设计表格?我听说一个字节用于列的长度或存储元数据。

现在重要吗?

我在互联网上看到了一些帖子,但是它们适用于Oracle和MySQL。

我们拥有Microsoft SQL Server 2016企业版,它如何应用于此环境?

现在说,例如,如果我告诉客户保留文本描述为255个字符而不是256个字符,会有什么区别吗?我读到的内容“最大长度为255个字符的DBMS可以选择使用单个字节来指示字段中数据的长度。如果限制为256或更大,则将需要两个字节。” 这是真的?


FYI:这个问题是横贴在MSDN论坛:social.msdn.microsoft.com/Forums/sqlserver/en-US/...
所罗门Rutzky

Answers:


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个字节。对于大值的某些组合,插入/更新操作将失败。

群集索引(以及SQL Server 2012和更早版本上的非群集索引)的最大键大小为900个字节。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_RowDumpCrackersys.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║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║
╚═════════════════════牛皮════════════牛皮═════════牛皮═════════ ══════牛皮══════════════════════════牛皮══════════牛皮══════════ ════════牛皮═════════════牛皮═════════牛皮═════════牛皮═══════ ══╝

如您所见,将InRowLength显示每个值的,以及每行的物理存储位置-“ file_id”,“ page_id”和“ slot_id”。

如果我们从上面的查询结果中获取file_idpage_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

我的机器的结果是:

PAGE:(1:1912)


缓冲:


BUF @ 0x00000000FF5B2E80

bpage = 0x0000000024130000 bhash = 0x0000000000000000 bpageno =(1:1912)
bdbid = 2 breferences = 0 bcputicks = 0
bsampleCount = 0 bUse1 = 32497 bstat = 0x10b
博客= 0​​x212121cc 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 DB Frag ID = 1                      

分配状态

GAM(1:2)=已分配SGAM(1:3)=未分配PFS(1:1)= 0x41已分配50_PCT_FULL
DIFF(1:6)= NOT CHANGED ML(1:7)= NOT 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 = 2222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222
22222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222
222222222222222222222222222222222222222222222                               

插槽0列3偏移量0x12c长度256长度(物理)256

varchar256 = 33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333
333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333
3333333333333333333333333333333333333333333333                              

16

其他人已经指出,存储长度所需的字节数是固定的。我想在您的问题中专注于这一部分:

现在重要吗?

您的问题标有企业版,这通常意味着您将拥有大量数据。实际上,每行一个字节的差异实际上并没有太大关系。例如,下表具有完全填充的VARCHAR(255)列,占用磁盘上的143176 KB空间:

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

声明的varchar大小不会影响性能。数据实际上可以通过页面压缩或行压缩存储为行存储。作为群集列存储或作为内存优化表。这些中的每一个都有不同的性能折衷,但是无论您声明varchar(255)还是varchar(256)都无关紧要。


9
@ DavidBrowne-Microsoft不,“声明的varchar大小不会影响性能”绝对不是正确的-数据类型的大小会影响查询的内存授予。有关更多详细信息,请参见brentozar.com/archive/2017/02/memory-grants-data-size
布伦特·奥扎尔

6
试图保持简单,不鼓励过早的优化。
David Browne-微软
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.