SQL Server Data Types Explained: Which One Should You Actually Use?

SQL Server Data Types Explained: Every Major Type, and Which One to Actually Use

Every column has exactly one data type — SQL Server enforces it strictly, unlike a spreadsheet cell that’ll happily hold text or a number interchangeably. This lesson covers every data type category you’ll realistically encounter: exact and approximate numbers, character strings, dates and times, binary data, and the “special purpose” types like UNIQUEIDENTIFIER and XML.

Choosing a Data Type(five families, one pick each)Exact NumbersINT, DECIMAL — precise, zero roundingApproximate NumbersFLOAT — fine for science, never moneyCharacter StringsNVARCHAR — safe default for all textDate & TimeDATETIME2 — the modern defaultBinary & SpecialVARBINARY, GUID, XML, spatial typesFLOAT is a binary approximation —it can silently produce $100.00000000001.Use DECIMAL for money. Always.

Exact Numeric Types

Use these when the value must be precisely correct — counts, IDs, money. No rounding error, ever.

Type Storage Range Use for
TINYINT 1 byte 0 to 255 Small counters, status codes, ages
SMALLINT 2 bytes -32,768 to 32,767 Small ranges — year, quantity in a typical order
INT 4 bytes ±2.1 billion The default choice for IDs, counts, foreign keys
BIGINT 8 bytes ±9.2 quintillion High-volume identity columns (event logs, telemetry) that will exceed 2.1 billion rows
DECIMAL(p,s) / NUMERIC(p,s) 5–17 bytes (depends on precision) Exact, defined by precision p and scale s Money, measurements, anything requiring exact arithmetic — DECIMAL and NUMERIC are functionally identical, DECIMAL is the conventional spelling
MONEY 8 bytes ±922 trillion, 4 decimal places Legacy currency type — most teams prefer DECIMAL(19,4) for portability and clearer rounding behavior
SMALLMONEY 4 bytes ±214,748.3648 Rarely used; same caveats as MONEY at a smaller range

Approximate Numeric Types

Type Storage Use for
FLOAT 4 or 8 bytes Scientific/statistical values where tiny binary rounding error is acceptable — never for money
REAL 4 bytes Lower-precision FLOAT(24); rarely chosen deliberately today

FLOAT is a binary approximation and can accumulate tiny rounding errors — summing many FLOAT values can produce $100.00000000001 instead of exactly $100.00. DECIMAL stores exact values with zero rounding error, which is why it’s the correct choice for financial data.

Character String Types

Type Storage Use for
CHAR(n) Fixed n bytes, space-padded Fixed-length codes — country codes, status flags (e.g. CHAR(2) for ‘US’)
VARCHAR(n) Variable, up to n bytes Variable-length ASCII/Latin text — names, addresses, single-language content
VARCHAR(MAX) Up to 2GB Long non-Unicode text — logs, descriptions
NCHAR(n) Fixed 2n bytes Fixed-length Unicode — rare
NVARCHAR(n) Variable, up to 2n bytes Variable-length Unicode text — the default recommendation for almost all text, since it safely holds any language or emoji
NVARCHAR(MAX) Up to 2GB Long Unicode text — articles, JSON payloads, free-form notes
TEXT / NTEXT Deprecated. Use VARCHAR(MAX) / NVARCHAR(MAX) instead

Rule of thumb: default to NVARCHAR. The storage cost difference versus VARCHAR is small, and it avoids an entire class of bugs where a user’s name or a customer’s address contains a character your ASCII column silently mangles.

Date and Time Types

Type Storage Precision Use for
DATE 3 bytes Day Birthdates, due dates — anything with no time component
TIME 3–5 bytes Up to 100ns A time of day with no date — daily opening hours, alarm times
SMALLDATETIME 4 bytes Minute Legacy; low precision, narrow range
DATETIME 8 bytes ~3ms, rounded Legacy — the older default; rounding quirks make DATETIME2 the better modern choice
DATETIME2 6–8 bytes Up to 100ns The modern default for date + time — wider range and better precision than DATETIME, at equal or smaller storage
DATETIMEOFFSET 8–10 bytes Up to 100ns Date + time + UTC offset — required whenever you need to store a time zone alongside the timestamp (multi-region applications)

Binary Types

Type Storage Use for
BINARY(n) Fixed n bytes Fixed-length raw bytes — hashes of a known fixed length
VARBINARY(n) Variable, up to n bytes Variable-length raw bytes — small file attachments, encrypted blobs
VARBINARY(MAX) Up to 2GB Large binary objects — documents, images stored in-database
IMAGE Deprecated. Use VARBINARY(MAX) instead

Other Special-Purpose Types

Type Use for
BIT True/false flags (0, 1, or NULL) — SQL Server packs up to 8 BIT columns into a single byte
UNIQUEIDENTIFIER A 16-byte GUID — useful for IDs that must be unique across multiple databases/systems without coordination, at the cost of larger, less sequential index keys than INT
XML Native XML storage with schema validation and XQuery support
SQL_VARIANT Stores a value of almost any base type in one column — rare, generally avoided since it defeats type-checking and indexing efficiency
ROWVERSION (formerly TIMESTAMP) An automatically-incrementing binary value per row, used for optimistic concurrency checks — not an actual date/time despite the old name
HIERARCHYID Compact encoding of a position in a tree — org charts, category trees
GEOGRAPHY / GEOMETRY Spatial types for round-earth (GEOGRAPHY) or planar (GEOMETRY) coordinates, with built-in distance/intersection methods

There’s no dedicated JSON type in SQL Server — JSON is stored as NVARCHAR(MAX) and manipulated with built-in functions like JSON_VALUE, JSON_QUERY, and ISJSON.

A Real Table Using a Representative Mix

CREATE TABLE dbo.Example_DataTypes (
    id            INT             IDENTITY(1,1) PRIMARY KEY,
    public_id     UNIQUEIDENTIFIER NOT NULL DEFAULT NEWID(),
    full_name     NVARCHAR(100)   NOT NULL,
    country_code  CHAR(2)         NOT NULL,
    price         DECIMAL(10,2)   NOT NULL,
    quantity      SMALLINT        NOT NULL,
    is_active     BIT             NOT NULL DEFAULT 1,
    signed_up_on  DATE            NOT NULL DEFAULT GETDATE(),
    last_login    DATETIME2       NULL,
    profile_json  NVARCHAR(MAX)   NULL,
    avatar        VARBINARY(MAX)  NULL,
    row_version   ROWVERSION
);

Why DECIMAL, Never FLOAT, for Money

This is one of the most common code-review flags in real SQL Server codebases: FLOAT’s binary rounding makes it fundamentally unsuitable for currency, while DECIMAL guarantees exact values.

The Rule of Thumb

Pick the smallest type that can never realistically overflow, and default to Unicode (NVARCHAR) for text and DATETIME2 for timestamps unless you have a specific reason not to. Using NVARCHAR(MAX) or BIGINT everywhere “to be safe” wastes storage and slows indexes down — you’ll see exactly why once we get to performance tuning.


Enjoyed this?

Subscribe to get every new SQL Server lesson as soon as it’s published, and share it with a developer who’d find it useful.

📡 Subscribe via RSS  | 
Share on X  | 
Share on LinkedIn  | 
Share on Facebook

Want the full structured course with quizzes, projects, and 10+ exercises per chapter? Check out SQL Server Fundamentals, coming soon on this site.