You have data. You need to get answers from it. SQL is the language that does this.

Every app you use — social media, banking, shopping — stores data in a database. SQL is how developers read, filter, and organize that data. It has been around since the 1970s, and it is still the most important skill for working with data.

What Is SQL?

SQL stands for Structured Query Language. It is a language for talking to databases. You write a query, and the database gives you the answer.

SQL is not a programming language like Python or JavaScript. You do not write loops or if-statements. Instead, you describe what you want, and the database figures out how to get it.

-- "Give me all books that cost less than 20 dollars"
SELECT title, price FROM books WHERE price < 20;

One line. The database does all the work.

What Is a Relational Database?

A relational database stores data in tables. A table looks like a spreadsheet. It has rows and columns.

  • Columns define what kind of data is stored (name, email, price)
  • Rows are the actual data entries (one row per book, one row per customer)

Here is an example books table:

| id | title                    | author_id | price | published_year |
|----|--------------------------|-----------|-------|----------------|
| 1  | The Great Gatsby         | 1         | 12.99 | 1925           |
| 2  | To Kill a Mockingbird    | 2         | 14.99 | 1960           |
| 3  | 1984                     | 3         | 11.99 | 1949           |
| 4  | Pride and Prejudice      | 4         | 9.99  | 1813           |
| 5  | The Catcher in the Rye   | 5         | 13.99 | 1951           |

Tables are related to each other. The author_id column in books connects to an authors table. This is why they are called “relational” databases.

Setting Up Your Practice Environment

You need a place to run SQL queries. The easiest option is SQLite. It is a lightweight database that runs on your computer with no setup.

Option 1: Online playground (no install)

Go to sqliteonline.com and start writing queries in your browser.

Option 2: SQLite on your computer

SQLite comes pre-installed on macOS and most Linux distributions. On Windows, download it from sqlite.org.

# Open SQLite
sqlite3 bookstore.db

This creates a new database file called bookstore.db.

Creating Our Sample Database

We will use an online bookstore database throughout this SQL series. Let us create the tables:

CREATE TABLE authors (
    id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    country TEXT
);

CREATE TABLE books (
    id INTEGER PRIMARY KEY,
    title TEXT NOT NULL,
    author_id INTEGER,
    price REAL,
    published_year INTEGER,
    in_stock INTEGER DEFAULT 1
);

CREATE TABLE customers (
    id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    email TEXT,
    city TEXT
);

Now let us add some data:

INSERT INTO authors (id, name, country) VALUES
(1, 'F. Scott Fitzgerald', 'USA'),
(2, 'Harper Lee', 'USA'),
(3, 'George Orwell', 'UK'),
(4, 'Jane Austen', 'UK'),
(5, 'J.D. Salinger', 'USA');

INSERT INTO books (id, title, author_id, price, published_year, in_stock) VALUES
(1, 'The Great Gatsby', 1, 12.99, 1925, 1),
(2, 'To Kill a Mockingbird', 2, 14.99, 1960, 1),
(3, '1984', 3, 11.99, 1949, 1),
(4, 'Pride and Prejudice', 4, 9.99, 1813, 0),
(5, 'The Catcher in the Rye', 5, 13.99, 1951, 1),
(6, 'Animal Farm', 3, 8.99, 1945, 1),
(7, 'Tender Is the Night', 1, 15.99, 1934, 0);

INSERT INTO customers (id, name, email, city) VALUES
(1, 'Alex Johnson', 'alex@example.com', 'New York'),
(2, 'Sam Wilson', 'sam@example.com', 'London'),
(3, 'Jordan Smith', 'jordan@example.com', 'New York'),
(4, 'Taylor Brown', NULL, 'Berlin');

We will use these tables in every SQL article. Copy and paste them into your playground.

SELECT — Getting Data from a Table

SELECT is the most common SQL command. It reads data from a table.

Get all columns from a table:

SELECT * FROM books;

The * means “all columns.” The result is every row and every column in the books table.

Get specific columns:

SELECT title, price FROM books;
| title                    | price |
|--------------------------|-------|
| The Great Gatsby         | 12.99 |
| To Kill a Mockingbird    | 14.99 |
| 1984                     | 11.99 |
| Pride and Prejudice      | 9.99  |
| The Catcher in the Rye   | 13.99 |
| Animal Farm              | 8.99  |
| Tender Is the Night      | 15.99 |

Always select only the columns you need. SELECT * is fine for learning, but in real applications, it is slower because it reads more data than necessary.

WHERE — Filtering Rows

WHERE filters rows based on a condition. Only rows that match the condition are returned.

SELECT title, price FROM books WHERE price > 12;
| title                    | price |
|--------------------------|-------|
| The Great Gatsby         | 12.99 |
| To Kill a Mockingbird    | 14.99 |
| The Catcher in the Rye   | 13.99 |
| Tender Is the Night      | 15.99 |

Only books with a price greater than 12 are shown.

Comparison Operators

SQL supports these comparison operators:

OperatorMeaning
=Equal to
!= or <>Not equal to
<Less than
>Greater than
<=Less than or equal to
>=Greater than or equal to
-- Books published in 1949
SELECT title FROM books WHERE published_year = 1949;

-- Books that are NOT in stock
SELECT title FROM books WHERE in_stock != 1;

-- Books published before 1950
SELECT title, published_year FROM books WHERE published_year < 1950;

Combining Conditions: AND, OR, NOT

You can combine multiple conditions with AND, OR, and NOT.

AND — both conditions must be true:

SELECT title, price FROM books
WHERE price > 10 AND in_stock = 1;
| title                    | price |
|--------------------------|-------|
| The Great Gatsby         | 12.99 |
| To Kill a Mockingbird    | 14.99 |
| 1984                     | 11.99 |
| The Catcher in the Rye   | 13.99 |

OR — at least one condition must be true:

SELECT title, price FROM books
WHERE price < 10 OR published_year > 1955;
| title                    | price |
|--------------------------|-------|
| To Kill a Mockingbird    | 14.99 |
| Pride and Prejudice      | 9.99  |
| Animal Farm              | 8.99  |

NOT — reverses a condition:

SELECT title FROM books WHERE NOT in_stock = 1;

This returns books that are NOT in stock.

Combining AND and OR:

Use parentheses to control the order:

SELECT title, price FROM books
WHERE (price < 10 OR price > 14) AND in_stock = 1;

Without parentheses, SQL evaluates AND before OR. This can give unexpected results. Always use parentheses when combining AND and OR.

IN, BETWEEN, and LIKE

These operators make filtering easier.

IN — matches any value in a list:

SELECT title FROM books
WHERE published_year IN (1925, 1949, 1960);
| title                    |
|--------------------------|
| The Great Gatsby         |
| To Kill a Mockingbird    |
| 1984                     |

This is shorter than writing published_year = 1925 OR published_year = 1949 OR published_year = 1960.

BETWEEN — matches a range (inclusive):

SELECT title, published_year FROM books
WHERE published_year BETWEEN 1940 AND 1960;
| title                    | published_year |
|--------------------------|----------------|
| To Kill a Mockingbird    | 1960           |
| 1984                     | 1949           |
| The Catcher in the Rye   | 1951           |
| Animal Farm              | 1945           |

BETWEEN includes both ends. So BETWEEN 1940 AND 1960 means >= 1940 AND <= 1960.

LIKE — pattern matching:

-- Books starting with "The"
SELECT title FROM books WHERE title LIKE 'The%';

-- Books containing "the" anywhere
SELECT title FROM books WHERE title LIKE '%the%';
  • % matches zero or more characters
  • _ matches exactly one character
-- Titles with exactly 4 characters
SELECT title FROM books WHERE title LIKE '____';

Note: LIKE is case-sensitive in some databases (PostgreSQL) and case-insensitive in others (SQLite, MySQL). Use ILIKE in PostgreSQL for case-insensitive matching.

IS NULL — Checking for Missing Data

NULL means “no value” or “unknown.” It is not the same as zero or an empty string. You cannot use = to check for NULL. You must use IS NULL or IS NOT NULL.

-- Customers without an email
SELECT name FROM customers WHERE email IS NULL;
| name         |
|--------------|
| Taylor Brown |
-- Customers who DO have an email
SELECT name FROM customers WHERE email IS NOT NULL;
| name           |
|----------------|
| Alex Johnson   |
| Sam Wilson     |
| Jordan Smith   |

A common beginner mistake is writing WHERE email = NULL. This never works. Always use IS NULL.

ORDER BY — Sorting Results

ORDER BY sorts the results. By default, it sorts in ascending order (smallest to largest, A to Z).

SELECT title, price FROM books ORDER BY price;
| title                    | price |
|--------------------------|-------|
| Animal Farm              | 8.99  |
| Pride and Prejudice      | 9.99  |
| 1984                     | 11.99 |
| The Great Gatsby         | 12.99 |
| The Catcher in the Rye   | 13.99 |
| To Kill a Mockingbird    | 14.99 |
| Tender Is the Night      | 15.99 |

Descending order (largest to smallest):

SELECT title, price FROM books ORDER BY price DESC;

Sort by multiple columns:

SELECT title, author_id, price FROM books
ORDER BY author_id ASC, price DESC;

This sorts by author_id first. If two books have the same author_id, they are sorted by price in descending order.

LIMIT — Limiting Results

LIMIT restricts how many rows are returned. This is useful for pagination or when you only need the top results.

-- Get the 3 cheapest books
SELECT title, price FROM books
ORDER BY price ASC
LIMIT 3;
| title                 | price |
|-----------------------|-------|
| Animal Farm           | 8.99  |
| Pride and Prejudice   | 9.99  |
| 1984                  | 11.99 |

LIMIT with OFFSET for pagination:

-- Skip the first 2, then get the next 3
SELECT title, price FROM books
ORDER BY price ASC
LIMIT 3 OFFSET 2;
| title                    | price |
|--------------------------|-------|
| 1984                     | 11.99 |
| The Great Gatsby         | 12.99 |
| The Catcher in the Rye   | 13.99 |

OFFSET 2 means “skip the first 2 rows.”

DISTINCT — Unique Values

DISTINCT removes duplicate values from the results.

SELECT DISTINCT country FROM authors;
| country |
|---------|
| USA     |
| UK      |

Without DISTINCT, you would see “USA” three times and “UK” twice.

SELECT DISTINCT city FROM customers;
| city      |
|-----------|
| New York  |
| London    |
| Berlin    |

Common Mistakes

1. Using = NULL instead of IS NULL

-- Wrong
SELECT * FROM customers WHERE email = NULL;

-- Correct
SELECT * FROM customers WHERE email IS NULL;

NULL is not a value. It means “unknown.” You cannot compare something to “unknown” with =.

2. Forgetting quotes around text values

-- Wrong
SELECT * FROM authors WHERE country = USA;

-- Correct
SELECT * FROM authors WHERE country = 'USA';

Text values need single quotes. Numbers do not.

3. Mixing up AND/OR without parentheses

-- This might not do what you expect
SELECT * FROM books WHERE price < 10 OR price > 14 AND in_stock = 1;

-- SQL reads this as: price < 10 OR (price > 14 AND in_stock = 1)
-- Use parentheses to be clear:
SELECT * FROM books WHERE (price < 10 OR price > 14) AND in_stock = 1;

AND has higher priority than OR. Always use parentheses when combining them.

What You Learned

In this article, you learned:

  • SQL is a language for querying databases
  • Tables store data in rows and columns
  • SELECT reads data from a table
  • WHERE filters rows with conditions
  • AND, OR, NOT combine conditions
  • IN, BETWEEN, LIKE are shortcuts for common filters
  • IS NULL checks for missing values
  • ORDER BY sorts results
  • LIMIT restricts how many rows you get
  • DISTINCT removes duplicates

What’s Next?

In the next article, you will learn how to add, change, and remove data with INSERT, UPDATE, and DELETE. You will also learn about transactions — how to make sure your changes are safe.