Storage calculations and VARCHAR

Concepts covered: Storage Optimization

An estimate needs 2 numbers: how many rows, and how many bytes per row. Bytes per row is the sum of the fixed widths from the type table plus the average size of each VARCHAR. The product is the decoded size, the bytes a query works through after it reads the files. Compression makes files smaller, so treat the figure as a ceiling for storage and as the working size whenever a query scans or sorts the data. Rows sent between machines travel at that size too.

Price one row of the Reels view log

ColumnTypeBytes per value
view_idBIGINT8
video_idBIGINT8
user_idBIGINT8
viewed_atTIMESTAMP8
watch_msINTEGER4
is_autoplayBOOLEAN1
countryVARCHAR6
Row total43

The country value is a 2-letter code: 2 bytes of text plus 4 bytes recording where the value ends. Variable-width data is commonly laid out with a 4-byte length or offset per value, so 4 bytes is a sound planning figure. Now scale it: suppose the table takes 10 billion rows a day, and run the arithmetic.

SELECT
  43 * 10000000000 AS bytes_per_day,
  43 * 10000000000 / 1e9 AS gb_per_day,
  43 * 10000000000 * 365 / 1e12 AS tb_per_year
Result
bytes_per_daygb_per_daytb_per_year
430000000000430156.95

Narrow widths pay off because they multiply. Here is what the 4 bytes INTEGER saves over BIGINT on watch_ms come to at that daily volume.

What the decoded estimate leaves out

A VARCHAR is priced in bytes

VARCHAR text is stored as UTF-8, which spends 1 to 4 bytes per character: 1 for ASCII letters and digits, 2 for accented Latin letters such as a with a tilde, 3 for most Chinese and Japanese characters, 4 for emoji. A column's byte cost follows the characters it holds.

ValueCharactersUTF-8 bytes
US22
Lisbon66
São Paulo910
東京26
🎬14

To price a VARCHAR column, sample it, find the average bytes per value and add the 4-byte length. A city column averaging 9 ASCII characters costs roughly 13 bytes a value; the same length in a script needing 3 bytes per character costs about 31. Storing country as 'United States' rather than 'US' takes it from 6 bytes to 17.

VARCHAR(n) caps length and truncates

A declared length reserves nothing: VARCHAR(255) holding 'US' costs what VARCHAR holding 'US' costs. The limit acts when text is converted. Casting longer text to a bounded VARCHAR keeps the first n characters and raises no error, so a limit set too low corrupts data quietly.

SELECT
  CAST('Brazil' AS VARCHAR(2)) AS country_code,
  CAST('São Paulo' AS VARCHAR(3)) AS city_prefix
Result
country_codecity_prefix
BrSão

Both results fit their limits. The limit counts characters, so 'São' keeps 3 characters while taking 4 bytes. A pipeline that casts incoming text to VARCHAR(2) would turn a source's 'Brazil' into 'Br', which matches none of the stored codes, without a single error.

A device_model column is declared VARCHAR(255). Its values average 12 ASCII characters across 2 billion rows. Using this section's model, about how large is the decoded column?

About this lesson

This section is part of the Data Types: Advanced lesson on DataDriven, the home of data engineering. Lessons cover SQL, Python, Spark, data modeling and pipeline architecture, and each section pairs its explanation with worked examples and practice you run as you go.