varchar (255) o varchar (256)?


21

¿Debo usar varchar(255)o varchar(256)al diseñar tablas? Escuché que se usa un byte para la longitud de la columna o para almacenar metadatos.

¿Importa más en este punto?

Vi algunas publicaciones en Internet, sin embargo, se aplican a Oracle y MySQL.

Tenemos Microsoft SQL Server 2016 Enterprise Edition, ¿cómo se aplica a este entorno?

Ahora, por ejemplo, qué pasa si les digo a mis clientes que guarden, por ejemplo, una descripción de texto de 255 caracteres en lugar de 256, ¿hay alguna diferencia? Lo que leí "Con una longitud máxima de 255 caracteres, el DBMS puede elegir usar un solo byte para indicar la longitud de los datos en el campo. Si el límite fuera 256 o más, se necesitarían dos bytes". ¿Es esto cierto?


FYI: esta pregunta fue publicada en los foros de MSDN: social.msdn.microsoft.com/Forums/sqlserver/en-US/…
Solomon Rutzky

Respuestas:


36

Dimensione cada columna apropiadamente. NO use un tamaño "estándar" para cada columna. Si solo necesita 30 caracteres, ¿por qué crear una columna que pueda manejar 255? Estoy muy contento de que no estés recomendando usar varchar(max)tus columnas de cadena.

Este es un consejo especialmente prudente si alguna vez necesita indexar una columna, o si está utilizando una columna como clave principal y tiene referencias de clave externa. SQL Server utiliza el tamaño de cada columna en su optimizador de consultas para comprender los requisitos de memoria estimados para el procesamiento de consultas. Tener columnas sobredimensionadas puede ser perjudicial para el rendimiento.

Los índices en columnas que son de gran tamaño pueden generar errores:

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

El intento de crear el índice anterior da como resultado esta advertencia:

¡Advertencia! La longitud máxima de la clave es de 900 bytes. El índice 'IX_WideIndex_01' tiene una longitud máxima de 1110 bytes. Para alguna combinación de valores grandes, la operación de inserción / actualización fallará.

900 bytes es el tamaño de clave máximo para los índices agrupados (y los índices no agrupados en SQL Server 2012 y versiones anteriores). 1700 bytes es el tamaño de clave máximo para índices no agrupados en versiones más recientes de SQL Server. Si diseña columnas con un ancho genérico, como (255), puede encontrar esta advertencia con mucha más frecuencia de lo esperado.

En caso de que esté interesado en elementos internos de almacenamiento, puede usar la siguiente pequeña prueba para comprender mejor cómo SQL Server almacena datos sin almacenar en filas.

Primero, crearemos una tabla donde podamos almacenar columnas de varios tamaños:

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

Ahora insertaremos una sola fila:

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

Esta consulta utiliza las funciones no documentadas y no compatibles, sys.fn_RowDumpCrackery sys.fn_PhyslocCrackerpara mostrar algunos detalles interesantes sobre la tabla:

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

La salida se verá similar a esto:

╔═════════════════════╦════════════╦═════════╦════ ══════╦══════════════════════════╦══════════╦═════ ════════╦═════════════╦═════════╦═════════╦═══════ ══╗
║ partición_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 ║
╚═════════════════════╩════════════╩═════════╩════ ══════╩══════════════════════════╩══════════╩═════ ════════╩═════════════╩═════════╩═════════╩═══════ ══╝

Como puede ver, InRowLengthse muestra el valor de cada valor, junto con la ubicación de almacenamiento físico de cada fila: "file_id", "page_id" y "slot_id".

Si tomamos los valores file_idy page_idde los resultados de la consulta anterior y los ejecutamos DBCC PAGE, podemos ver el contenido real de la página física:

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

Los resultados de mi máquina son:

PÁGINA: (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          

ENCABEZADO DE PÁGINA:


Página @ 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
Metadatos: AllocUnitId = 2233785421652951040                              
Metadatos: PartitionId = 1945555045333008384 Metadatos: IndexId = 0
Metadatos: 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 de Fragmento de DB = 1                      

Estado de asignación

GAM (1: 2) = SGAM ASIGNADO (1: 3) = PFS NO ASIGNADO (1: 1) = 0x41 ASIGNADO 50_PCT_FULL
DIFF (1: 6) = NO CAMBIADO ML (1: 7) = NO MIN_LOGGED           

Ranura 0 Offset 0x60 Longitud 556

Tipo de registro = PRIMARY_RECORD Atributos de registro = NULL_BITMAP VARIABLE_COLUMNS
Tamaño de registro = 556                   
Volcado de memoria @ 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

Ranura 0 Columna 1 Desplazamiento 0xf Longitud 30 Longitud (física) 30

varchar30 = 111111111111111111111111111111                               

Ranura 0 Columna 2 Desplazamiento 0x2d Longitud 255 Longitud (física) 255

varchar255 = 2222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222
22222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222
222222222222222222222222222222222222222222                               

Ranura 0 Columna 3 Desplazamiento 0x12c Longitud 256 Longitud (física) 256

varchar256 = 3333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333
33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333
3333333333333333333333333333333333333333333                              

16

Otros ya han señalado que el número de bytes necesarios para almacenar la longitud es fijo. Quería centrarme en esta parte en su pregunta:

¿Importa más en este punto?

Tiene su pregunta etiquetada con la edición empresarial, lo que generalmente significa que tendrá una buena cantidad de datos. A menudo, las diferencias de un byte por fila realmente no importan demasiado en la práctica. Por ejemplo, la siguiente tabla con una VARCHAR(255)columna completamente ocupada ocupa 143176 KB de espacio en el 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';

Resultados:

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

Creemos una segunda tabla con una VARCHAR(256)columna completamente llena . Eso tomará al menos un byte más por fila, ¿verdad?

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

Resultados:

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

Sucede que ambas tablas ocupan la misma cantidad de espacio. Se ajusta el mismo número de filas en cada página de 8k. Es genial que quieras pasar tiempo optimizando tu aplicación, pero sospecho que es mejor que te concentres en diferentes áreas.


7

El tamaño declarado del varchar no tiene impacto en el rendimiento. Los datos pueden almacenarse realmente como un almacén de filas con compresión de página o compresión de fila. Como un almacén de columnas en clúster, o como una tabla con memoria optimizada. Cada uno de estos tendrá diferentes compensaciones de rendimiento, pero nunca importa si declara un varchar (255) o varchar (256).


99
@ DavidBrowne-Microsoft no, "el tamaño declarado del varchar no tiene impacto en el rendimiento" definitivamente no es cierto: el tamaño del tipo de datos afecta a las concesiones de memoria para consultas. Ver brentozar.com/archive/2017/02/memory-grants-data-size para más detalles.
Brent Ozar

66
Tratando de mantenerlo simple y desalentar la optimización prematura.
David Browne - Microsoft
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.