SQLab Hub
Lesson 1 · Data Types
Lesson 1 · Fundamentals

SQL Data Types: INT, VARCHAR, DECIMAL, DATE and More

A column's data type says what kind of value it holds: whole numbers, exact decimals, text, dates or true/false. The type decides what you can do with the values (add them, sort them as dates, compare them) and what can go wrong, such as a phone number losing its leading +.

INT · DECIMAL · VARCHAR · DATE Choosing the right type ▶ Runnable Playground examples 🧪 4 practice exercises 📝 6-question quiz ⏱ ~15 min

What Are SQL Data Types?

Every column is declared with a type when the table is created. You can see the declared types of a Playground table with SQLite's pragma_table_info:

SQLite · runs in the Playground
SELECT name AS column_name, type AS declared_type
FROM pragma_table_info('orders');

▶ Run this query in the SQL Playground

Columns and declared types of the orders table
column_namedeclared_type
idINTEGER
user_idINTEGER
totalREAL
discountREAL
item_countINTEGER
statusTEXT
order_dateTEXT
shipped_dateTEXT
shipping_cityTEXT
shipping_countryTEXT
✓ 10 rows · real output from the Playground database
TypeHoldsExample valueTypical columns
INT / INTEGERWhole numbers42ids, quantities, counts
DECIMAL(p, s) / NUMERICExact numbers with s decimal places1299.99prices, salaries, money
FLOAT / REAL / DOUBLEApproximate numbers0.30000000000000004measurements, scientific values
VARCHAR(n)Text up to n characters'Austin'names, emails, codes, phone numbers
CHAR(n)Text of exactly n characters, padded'US'fixed-length codes
TEXTLong texta review commentdescriptions, comments
DATE / DATETIME / TIMESTAMPDates and times'2024-12-24'order dates, created_at
BOOLEANTrue or falseTRUEflags such as is_premium

INT, DECIMAL and FLOAT

Integers have no fractional part. DECIMAL stores an exact number of decimal places: DECIMAL(10,2) means up to 10 digits in total, 2 of them after the point, so up to 99,999,999.99. FLOAT and REAL store approximate binary values, which causes tiny rounding errors:

SQLite · runs in the Playground
SELECT 0.1 + 0.2        AS float_sum,
       0.1 + 0.2 = 0.3  AS equals_0_3,
       ROUND(0.1 + 0.2, 2) AS rounded;

▶ Run this query in the SQL Playground

Floating-point arithmetic in SQLite
float_sumequals_0_3rounded
0.3000000000000000400.3
✓ 1 row · real output from the Playground database

The sum is not exactly 0.3, so the comparison is false (0). For money that is not acceptable: rounding errors add up over millions of rows and make totals fail to reconcile. Use DECIMAL for prices, salaries and balances.

A note on the Playground. The Playground stores orders.total and products.price as REAL, a floating-point type, because SQLite has no true DECIMAL storage. That is fine for practice. In a production MySQL, PostgreSQL or SQL Server database, money columns should be DECIMAL.
TypeRange (typical)Storage
SMALLINT-32,768 to 32,7672 bytes
INTAbout ±2.1 billion4 bytes
BIGINTAbout ±9.2 quintillion8 bytes
DECIMAL(10,2)Up to 99,999,999.99, exactAbout 5 to 9 bytes

VARCHAR, CHAR and TEXT

VARCHAR(100) stores up to 100 characters and uses only the space the value needs. CHAR(100) always stores 100, padding short values with spaces, which suits fixed-length codes like country codes. TEXT holds long text.

Phone numbers are text, not numbers

A phone number is an identifier, not a quantity: you never add two of them. Stored as an integer, the formatting, the + and any leading zeros are lost. Here is what converting to INTEGER does:

SQLite · runs in the Playground
SELECT phone,
       CAST(phone AS INTEGER) AS phone_as_integer,
       CAST('007' AS INTEGER) AS leading_zeros_as_integer
FROM users
LIMIT 3;

▶ Run this query in the SQL Playground

Phone numbers and codes converted to integers
phonephone_as_integerleading_zeros_as_integer
+1-263-376-237117
+1-753-780-667017
+1-440-743-950317
✓ 3 rows · real output from the Playground database

SQLite reads the number up to the first character that is not a digit, so +1-263-376-2371 becomes 1. Other databases reject the conversion or behave differently, but none keep the original value. The same applies to postal codes, account numbers and SKUs.

DATE, DATETIME and TIMESTAMP

TypeHoldsExample
DATEA calendar date2024-12-24
TIMEA time of day14:30:00
DATETIME / TIMESTAMPDate and time2024-12-24 14:30:00
TIMESTAMP WITH TIME ZONE (PostgreSQL: timestamptz)A moment, time-zone aware2024-12-24 14:30:00+00

With a real date type the database can compare, sort and calculate with dates. SQLite has no date type: dates are text, and they only behave correctly in YYYY-MM-DD form. You can see that in the Playground:

SQLite · runs in the Playground
SELECT typeof(order_date) AS stored_as,
       MIN(order_date) AS first_order,
       MAX(order_date) AS last_order,
       SUM(order_date >= '2024-01-01') AS orders_in_2024
FROM orders;

▶ Run this query in the SQL Playground

How the Playground stores and compares dates
stored_asfirst_orderlast_orderorders_in_2024
text2022-01-062024-12-2466
✓ 1 row · real output from the Playground database

Text in another format, such as 12/24/2024, would sort by month first and break range filters. Store dates in a date type, or at least in ISO format. See date functions.

BOOLEAN

PostgreSQL has a real BOOLEAN type. MySQL's BOOLEAN is TINYINT(1), SQL Server uses BIT, and SQLite stores 0 and 1 in an INTEGER column, as the Playground does with is_premium:

SQLite · runs in the Playground
SELECT is_premium, COUNT(*) AS users
FROM users
GROUP BY is_premium;

▶ Run this query in the SQL Playground

A 0/1 flag column
is_premiumusers
081
169
✓ 2 rows · real output from the Playground database

How SQLite Handles Types

SQLite is unusual: a declared type is a preference (an "affinity"), not a strict rule, and each value carries its own storage class. typeof() shows it:

SQLite · runs in the Playground
SELECT typeof(id) AS id_type,
       typeof(total) AS total_type,
       typeof(status) AS status_type,
       typeof(shipped_date) AS shipped_date_type
FROM orders
WHERE id = 1;

▶ Run this query in the SQL Playground

Storage class of values in one order
id_typetotal_typestatus_typeshipped_date_type
integerrealtextnull
✓ 1 row · real output from the Playground database

An order that has not shipped has NULL there, which is its own storage class. MySQL, PostgreSQL and SQL Server enforce column types much more strictly: inserting 'abc' into an INT column is an error. SQLite has STRICT tables (3.37+) for the same behaviour.

Choosing the Right Type

ColumnGood typeWhy
idINT or BIGINT (or UUID)Small, fast to compare and index
price, salary, balanceDECIMAL(12,2)Exact, no rounding errors
quantity, stockINTWhole numbers you add up
emailVARCHAR(255)Addresses can be up to 254 characters
phone, postal code, SKUVARCHAR(20) or similarIdentifiers with leading zeros, + and dashes
country codeCHAR(2)Always two letters
order dateDATEDate comparisons and arithmetic
created atTIMESTAMP (with time zone where available)An exact moment
is_premiumBOOLEAN (BIT, TINYINT(1))Two states
review textTEXTLong and variable

Type Names in Each Database

ConceptMySQLPostgreSQLSQL ServerSQLite
Whole numberINTINTEGERINTINTEGER
Exact decimalDECIMALNUMERIC / DECIMALDECIMALNUMERIC affinity (stored as integer or real)
TextVARCHAR(n), TEXTVARCHAR(n), TEXTVARCHAR(n), NVARCHAR(n)TEXT
Date and timeDATE, DATETIME, TIMESTAMPDATE, TIMESTAMP, TIMESTAMPTZDATE, DATETIME2, DATETIMEOFFSETTEXT, REAL or INTEGER
BooleanBOOLEAN (TINYINT(1))BOOLEANBITINTEGER 0/1
Auto-increment idINT AUTO_INCREMENTGENERATED ... AS IDENTITY / SERIALINT IDENTITYINTEGER PRIMARY KEY
SQL · PostgreSQL / MySQL (run in a local database)
CREATE TABLE products (
  id         INT PRIMARY KEY,
  name       VARCHAR(100) NOT NULL,
  sku        VARCHAR(20)  UNIQUE,
  price      DECIMAL(10,2) NOT NULL,
  stock      INT DEFAULT 0,
  is_active  BOOLEAN DEFAULT TRUE,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Common Data Type Mistakes

Five errors and how to avoid them.

1. Money in FLOAT

❌ Rounding errors
SQL
price FLOAT
✅ Exact decimals
SQL
price DECIMAL(10,2)

0.1 + 0.2 is not exactly 0.3 in floating point, as the example above shows.

2. Phone numbers as INT

❌ Loses + and leading zeros
SQL
phone INT
✅ Text
SQL
phone VARCHAR(20)

Phone numbers are identifiers. The Playground example turns +1-263-376-2371 into 1.

3. Dates as free-form text

❌ Sorts and filters wrongly
SQL
order_date VARCHAR(20)  -- '12/24/2024'
✅ A date type
SQL
order_date DATE

Text compares character by character: '12/24/2023' sorts after '02/01/2024'.

4. VARCHAR for everything

❌ No arithmetic, no validation
SQL
quantity VARCHAR(10)
✅ The type that fits the data
SQL
quantity INT

'10' < '9' as text. Numbers stored as text sort wrongly and need conversion before SUM.

5. Too-small sizes

❌ Values cut off or rejected
SQL
email VARCHAR(30)
✅ Room for real data
SQL
email VARCHAR(255)

Long but valid addresses fail to insert, or are truncated in databases that allow it.

Practice Data Types Queries

4 exercises on the real Playground tables.

Write each query yourself first. Every solution has been run against the Playground database.

  1. 1Easy List the columns of the products table and their declared types. Table: products

    Show solution
    SQLite · runs in the Playground
    SELECT name AS column_name, type AS declared_type
    FROM pragma_table_info('products');

    ▶ Run this query in the SQL Playground

    pragma_table_info is SQLite's way to read a table definition; other databases use information_schema.columns.

  2. 2Easy Show the storage class of the salary, hire_date and manager_id values for employee 1. Table: employees

    Show solution
    SQLite · runs in the Playground
    SELECT typeof(salary) AS salary_type,
           typeof(hire_date) AS hire_date_type,
           typeof(manager_id) AS manager_id_type
    FROM employees
    WHERE id = 1;

    ▶ Run this query in the SQL Playground

    Employee 1 has no manager, so manager_id is NULL.

  3. 3Medium Show what happens to a SKU such as SKU-000042 when it is converted to an integer, and how to get its number correctly. Table: products

    Show solution
    SQLite · runs in the Playground
    SELECT sku,
           CAST(sku AS INTEGER) AS cast_whole_sku,
           CAST(SUBSTR(sku, 5) AS INTEGER) AS sku_number
    FROM products
    WHERE id = 42;

    ▶ Run this query in the SQL Playground

    The whole SKU starts with letters, so it converts to 0. Take the digits first with SUBSTR.

  4. 4Medium Show why numbers stored as text sort wrongly: compare '10' < '9' as text with 10 < 9 as numbers. Table: (none)

    Show solution
    SQLite · runs in the Playground
    SELECT '10' < '9' AS text_comparison,
           10 < 9     AS number_comparison;

    ▶ Run this query in the SQL Playground

    As text, '10' comes before '9' because '1' is less than '9', so the first comparison is true (1). As numbers it is false (0).

📝 Quiz — Data Types

Test your understanding · 0/6 answered

Question 1 of 6

Which data type is best for a person's age?

Question 2 of 6

Which data type should store a price such as 1299.99?

Question 3 of 6

What goes wrong if phone numbers are stored as INT?

Question 4 of 6

What is the difference between VARCHAR(100) and CHAR(100)?

Question 5 of 6

Which type records both the date and the exact time of a login?

Question 6 of 6

Which table definition uses suitable types?

Data Types Interview Questions

Q1. Why use DECIMAL instead of FLOAT for money?

FLOAT stores binary approximations, so values like 0.1 cannot be represented exactly and sums drift. DECIMAL stores exact decimal digits.

Q2. VARCHAR or CHAR?

VARCHAR for values of varying length; CHAR only for truly fixed-length values such as two-letter country codes, because CHAR pads shorter values with spaces.

Q3. How does SQLite treat types differently?

Declared types are affinities, not strict rules, and each value has its own storage class (NULL, INTEGER, REAL, TEXT, BLOB). Dates are stored as text or numbers. STRICT tables enforce types.

Q4. Why should dates not be stored as text?

Text compares character by character, so only ISO YYYY-MM-DD text sorts correctly, and date arithmetic needs conversion. A date type sorts, compares and calculates correctly and rejects invalid dates.

Frequently Asked Questions

What are SQL data types?

Data types define what kind of value a column can hold: numbers (INT, DECIMAL), text (VARCHAR, CHAR, TEXT), dates (DATE, DATETIME) or true/false (BOOLEAN). The type is set when the table is created. Most databases enforce it on every insert and update; SQLite treats it as a preference unless the table is STRICT.

Should I store phone numbers as INT or VARCHAR?

VARCHAR. Phone numbers can start with +, contain dashes or spaces and have leading zeros. Stored as an integer they lose all of that.

What is the difference between INT and DECIMAL?

INT stores whole numbers. DECIMAL stores exact numbers with a fixed number of decimal places: DECIMAL(10,2) allows up to 10 digits, 2 of them after the point. Use DECIMAL for prices and salaries.

Why not just use VARCHAR for everything?

Text cannot be added up, sorts numbers wrongly ('10' comes before '9') and does not validate dates or numbers. The right type gives you correct sorting, arithmetic and validation.

Why is DECIMAL better than FLOAT for money?

FLOAT uses binary floating point, so 0.1 + 0.2 gives 0.30000000000000004 rather than 0.3, as the Playground shows. DECIMAL stores exact values.

Where can I practise writing CREATE TABLE with correct types?

The SQLabHub Playground only runs SELECT and WITH queries, so CREATE TABLE needs a local database. SQLite (with the sqlite3 command-line tool or DB Browser for SQLite), PostgreSQL and MySQL are all free to install. In the Playground you can inspect the existing tables' types with pragma_table_info.

What SQL data type should I use to store money?

DECIMAL, for example DECIMAL(10,2) for ordinary prices or DECIMAL(19,4) where more precision is needed. Avoid FLOAT for money.

Can phone numbers be stored as INT in SQL?

They should not be. An integer loses the leading +, any leading zeros and the formatting. Use VARCHAR(20) or similar.

Is VARCHAR slower than INT for SQL queries?

Comparing and indexing integers is generally cheaper than comparing text, and integer indexes are smaller. The bigger point is correctness: store numbers as numbers and identifiers as text.

Which SQL data type uses the least memory?

For a flag, BOOLEAN (1 byte in MySQL as TINYINT(1); SQL Server packs up to 8 BIT columns into one byte). SMALLINT uses 2 bytes, INT 4 and BIGINT 8. VARCHAR uses the length of the value plus 1 or 2 bytes.

Should a salary column use FLOAT or DECIMAL?

DECIMAL, such as DECIMAL(12,2). Payroll needs exact cents, and floating-point rounding errors cause totals that do not reconcile.

Can SQL dates be stored as VARCHAR text?

It is possible, and SQLite always stores dates as text, but in databases with a date type you should use it. Text dates only sort and compare correctly in YYYY-MM-DD form, and different formats of the same date are not equal.

What data type should I use for database record IDs?

INT for most tables (about 2.1 billion values), BIGINT for very large ones. For ids generated in many places without a central counter, use a UUID type or CHAR(36).

What is the best SQL data type for storing email addresses?

VARCHAR(255). An email address can be up to 254 characters. CHAR(255) would pad every address with spaces, and VARCHAR is easy to index.