Reading data is the core of SQL. Every real question  “Who signed up today?†or “Top 10 customers by spend?† is answered by picking the right table, the right columns, the right rows, and the right order.
Once you understand what a database and SQL are, the next step is learning how to read data. Commands like SELECT, FROM, WHERE, logical operators (AND/OR/NOT), comparison operators, ORDER BY, LIMIT, DISTINCT, and AS (aliases) work together to pick the right rows, choose the right columns, and present results clearly.
At a high level, every basic query answers:
FROMSELECTWHEREORDER BYLIMIT / DISTINCTcountries TableTo make every command concrete, we will use one relatable table throughout this lesson.
CREATE TABLE countries (
id INT PRIMARY KEY AUTO_INCREMENT,
country VARCHAR(64) NOT NULL,
population BIGINT NULL, area INT NOT NULL,
region VARCHAR(64) NOT NULL
);
INSERT INTO countries (country, population, area, region) VALUES
('India', 1400000000, 3287000, 'Asia'),
('China', 1200000000, 4287000, 'Asia'),
('Brazil', 214000000, 8516000, 'South America'),
('Australia', 25700000, 7692000, 'Oceania'),
('USA', 331000000, 9834000, 'North America');
CREATE TABLE countries (
id SERIAL PRIMARY KEY,
country VARCHAR(64) NOT NULL,
population BIGINT NULL, area INT NOT NULL,
region VARCHAR(64) NOT NULL
);
INSERT INTO countries (country, population, area, region) VALUES
('India', 1400000000, 3287000, 'Asia'),
('China', 1200000000, 4287000, 'Asia'),
('Brazil', 214000000, 8516000, 'South America'),
('Australia', 25700000, 7692000, 'Oceania'),
('USA', 331000000, 9834000, 'North America');
| id | country | population | area | region |
|---|---|---|---|---|
| 1 | India | 1400000000 | 3287000 | Asia |
| 2 | China | 1200000000 | 4287000 | Asia |
| 3 | Brazil | 214000000 | 8516000 | South America |
| 4 | Australia | 25700000 | 7692000 | Oceania |
| 5 | USA | 331000000 | 9834000 | North America |
SELECT specifies which columns you want. FROM tells SQL which table to read.
SELECT *
FROM countries;
SELECT *
FROM countries;
Useful when exploring a new table, but prefer explicit columns in real code.
| id | country | population | area | region |
|---|---|---|---|---|
| 1 | India | 1400000000 | 3287000 | Asia |
| 2 | China | 1200000000 | 4287000 | Asia |
| 3 | Brazil | 214000000 | 8516000 | South America |
| 4 | Australia | 25700000 | 7692000 | Oceania |
| 5 | USA | 331000000 | 9834000 | North America |
SELECT country, population
FROM countries;
SELECT country, population
FROM countries;
| country | population |
|---|---|
| India | 1400000000 |
| China | 1200000000 |
| Brazil | 214000000 |
| Australia | 25700000 |
| USA | 331000000 |
Result is narrower and focused  build the habit: only select what you need.
WHERE keeps only rows that satisfy a condition.
SELECT *
FROM countries
WHERE region = 'Oceania';
SELECT *
FROM countries
WHERE region = 'Oceania';
| country | population | area | region |
|---|---|---|---|
| Australia | 25700000 | 7692000 | Oceania |
WHERE decides which rows stay.
| Operator | Meaning | Example |
|---|---|---|
< | Less than | population < 50000000 |
> | Greater than | salary > 30000 |
<= | Less than or equal to | age <= 18 |
>= | Greater than or equal to | marks >= 90 |
= | Equal to | city = 'Delhi' |
<> / != | Not equal to | status != 'active' |
These form the foundation of filtering. We will use them in every query ahead.
Combine or modify conditions in WHERE.
Population > 50M and area < 5M sq km:
SELECT *
FROM countries
WHERE population > 50000000 AND area < 5000000;
SELECT *
FROM countries
WHERE population > 50000000 AND area < 5000000;
| country | population | area | region |
|---|---|---|---|
| India | 1400000000 | 3287000 | Asia |
| China | 1200000000 | 4287000 | Asia |
Nations in Asia or Oceania:
SELECT *
FROM countries
WHERE region = 'Asia' OR region = 'Oceania';
SELECT *
FROM countries
WHERE region = 'Asia' OR region = 'Oceania';
| country | population | area | region |
|---|---|---|---|
| India | 1400000000 | 3287000 | Asia |
| China | 1200000000 | 4287000 | Asia |
| Australia | 25700000 | 7692000 | Oceania |
Exclude North America:
SELECT *
FROM countries
WHERE NOT region = 'North America';
-- equivalent
SELECT * FROM countries WHERE region <> 'North America';
SELECT *
FROM countries
WHERE NOT region = 'North America';
| country | population | area | region |
|---|---|---|---|
| India | 1400000000 | 3287000 | Asia |
| China | 1200000000 | 4287000 | Asia |
| Brazil | 214000000 | 8516000 | South America |
| Australia | 25700000 | 7692000 | Oceania |
Which query finds Asian countries with population > 500M?
| Operator | Definition | Example |
|---|---|---|
+ | Addition | SELECT 10 + 5 AS result; |
- | Subtraction | SELECT 10 - 5 AS result; |
* | Multiplication | SELECT 10 * 5 AS result; |
/ | Division | SELECT 10 / 5 AS result; |
% | Modulus (remainder) | SELECT 10 % 3 AS remainder; |
DIV | Integer division (MySQL-only) | SELECT 7 DIV 2 AS quotient; -- 3 |
NULLIF(value1, value2) returns NULL if the two values are equal, otherwise value1. Common use: prevent division-by-zero  convert 0 to NULL so division yields NULL instead of an error.
Syntax: NULLIF(value1, value2)
SELECT
country,
population / NULLIF(area, 0) AS density
FROM countries;
SELECT
country,
population / NULLIF(area, 0) AS density
FROM countries;
| country | density |
|---|---|
| India | 425.920292 |
| China | 279.916025 |
| Brazil | 25.129169 |
| Australia | 3.341134 |
| USA | 33.658735 |
If a test row had area = 0, NULLIF(area, 0) → NULL, so population / NULL → NULL safely instead of an error.
ORDER BY sorts rows. Default is ASC (small → large, A → Z). Use DESC for reverse (large → small, Z → A). LIMIT returns only the top N rows.
Top 2 countries by land area (largest first):
SELECT country, area
FROM countries
ORDER BY area DESC
LIMIT 2;
SELECT country, area
FROM countries
ORDER BY area DESC
LIMIT 2;
-- or FETCH FIRST 2 ROWS ONLY
| country | area |
|---|---|
| USA | 9834000 |
| Brazil | 8516000 |
First sort by area DESC, then keep only the top 2 via LIMIT.
DISTINCT removes duplicates  keep only unique values. AS gives a temporary, reader-friendly name to a column or table without changing the schema.
Unique regions, renamed to unique_region:
SELECT DISTINCT region AS unique_region
FROM countries;
SELECT DISTINCT region AS unique_region
FROM countries;
| unique_region |
|---|
| Asia |
| South America |
| Oceania |
| North America |
What do ORDER BY area DESC LIMIT 2 and DISTINCT do?
SELECT cols FROM table WHERE rows ORDER BY sort LIMIT n
SELECT = which columns; FROM = which table; WHERE = which rows stay
ORDER BY ASC/DESC; LIMIT top N; DISTINCT = unique only; AS = alias
Comparison: < > <= >= = <>/!=
Logical: AND (all true), OR (any true), NOT (negates)
WHERE region='Oceania'; WHERE pop>50M AND area<5M; WHERE region='Asia' OR region='Oceania'; WHERE NOT region='North America'
+ - * / % ; DIV = MySQL integer division (7 DIV 2 = 3)
NULLIF(v1,v2) → NULL if equal else v1  safe divide: population / NULLIF(area,0) → NULL not error
CREATE TABLE countries (id SERIAL PK, country, population BIGINT, area INT, region)
SELECT * vs SELECT country, population  prefer explicit columns
ORDER BY area DESC LIMIT 2 → USA, Brazil; DISTINCT region AS unique_region → 4 regions
Primary sources: MySQL SELECT · Postgres SELECT · Postgres NULLIF. Ask me anything that's unclear.
Questions? Ask your agent  you can follow up on any concept, quiz answer, or how this applies to a specific query. Review the SQL glossary or syntax quick reference for a compressed overview.