Skip to content
,
SQL series · Part 3

SQL Tutorial 3: Retrieving Data with SELECT

11 min read
Featured cover for SQL Tutorial 3: Retrieving Data with SELECT

When you retrieve data with SELECT, name the exact columns you need instead of grabbing them all. SQL (the language used to ask databases for data) gives you a few small tools for this: named columns, new names for columns in the result, removing repeated rows, and showing only a few rows while you explore.

Say you write SELECT * (which means “every column”) because it feels safe. Then the result is 80 columns wide, the CSV file (a plain spreadsheet file with values separated by commas) is too big to email, and your manager asks why the export includes three internal ID columns and a test flag from 2019.

This tutorial is part of our SQL series on Analytics Made Simple. We stay on reading data, but we make each SELECT query (a request for data) intentional: named columns, aliases that make sense in a slide deck, DISTINCT when duplicates are noise, and LIMIT when you are exploring. The toy tables are still customers and orders. Series hub: SQL series.

What you will learn

  • How SELECT lists specific columns instead of hauling everything.
  • When SELECT * is fine, and when it is lazy in a harmful way.
  • How aliases rename columns in the result without changing the table.
  • How DISTINCT collapses duplicate result rows.
  • How LIMIT caps how many rows you fetch while exploring.
  • How these tools combine for clean handoffs to spreadsheets, business intelligence (BI) tools, the apps that turn tables into charts and reports, or Python.

SELECT is a shopping list

The SELECT clause is not decoration. It is the shopping list of values you want back. Everything after it shapes which rows and in what order, but the list decides what each result row contains.

Minimal reminder from earlier parts:

SELECT
  name,
  region
FROM customers;

Two columns only. No email, no id, unless you asked. That is already a product decision: what does this handoff need?

Name your columns

Prefer explicit column lists for anything you share, schedule, or put behind a dashboard (a screen of charts that updates on its own).

SELECT
  customer_id,
  name,
  region,
  email
FROM customers
ORDER BY customer_id;

Example output:

customer_idnameregionemail
1AnaEasta@x.com
2LeeWestl@x.com
3SamEasts@x.com
customers sample

Why explicit lists win:

  • Stability. If someone adds a huge text column to the table tomorrow, your extract does not suddenly balloon.
  • Clarity. Readers see the contract: these are the fields this query cares about.
  • Safety. You are less likely to leak columns you did not mean to export.
  • Performance benefits often follow as well. Wide tables with heavy columns cost more to ship over the network. You do not need every byte for every question.

When SELECT * is still acceptable

  • Private exploration on a small practice table.
  • Quick “what columns even exist?” moments in a client that does not show a schema browser (a panel listing the tables and their columns).
  • Throwaway debugging you will not save as a production query.

A good habit: start with * for thirty seconds, then rewrite with names before you paste the query into a shared doc.

Expressions in the select list

You are not limited to raw columns. You can compute light mathematical expressions directly, but always keep them readable.

SELECT
  order_id,
  amount,
  amount * 1.08 AS amount_with_tax_estimate
FROM orders
WHERE status = 'paid';

That tax example is toy math, not tax advice. The point is structural: the result can include derived values. Heavy business logic often belongs in documented models, not in one-off mystery expressions, but small transforms are normal.

Aliases: rename for humans

An alias renames a column (or expression) in the result. The underlying table stays the same. Syntax uses AS (recommended for clarity).

SELECT
  customer_id AS id,
  name AS customer_name,
  region AS sales_region
FROM customers
ORDER BY id;

Example output:

nameemail
Anaa@x.com
Leel@x.com
Sams@x.com
Example output: SELECT name, email

Aliases help when:

  • Database names are cryptic (cust_nm) and humans need English.
  • Two tables share a column name after a join (our upcoming joins guide) and you must disambiguate.
  • You compute COUNT(*) and want AS order_count instead of a blank or ugly default label.

Style tips:

  • Prefer snake_case aliases for data work; they travel well into Python and files.
  • Avoid spaces in aliases unless your team standard requires quoted column titles for a business intelligence tool, because names with spaces must be quoted every time someone reuses them.
  • Do not alias everything just to feel busy. Alias when the default name is unclear.

DISTINCT: unique result rows

DISTINCT removes duplicate rows from the result after the select list is built. It is not a magic dedupe for bad source data. It is a way to ask, “What unique combinations appear?”

Which regions appear in the customer table?

SELECT DISTINCT
  region
FROM customers
ORDER BY region;

Example output:

region
East
West
Example output: SELECT DISTINCT region

Unique region and status pairs from a join-like question on orders alone (status only here):

SELECT DISTINCT
  status
FROM orders
ORDER BY status;

DISTINCT across multiple columns treats the whole combination as the uniqueness key:

SELECT DISTINCT
  customer_id,
  status
FROM orders
ORDER BY customer_id, status;

That answers: which statuses has each customer ever had on an order? It does not answer how many times. For row counts, you want aggregate functions (our aggregates guide), not DISTINCT alone.

DISTINCT is not a substitute for keys

If your customer table has two rows for the same person with different ids, DISTINCT on email might hide a data quality problem instead of fixing it. When duplicates are a reliability issue, fix upstream or document a merge rule. See the data quality series for uniqueness as a dimension, not only a SELECT keyword.

LIMIT: take a sample, not the ocean

LIMIT caps how many rows return. Perfect for exploration, smoke tests, and “show me an example.”

SELECT
  order_id,
  customer_id,
  amount,
  status
FROM orders
ORDER BY order_date DESC
LIMIT 5;

Important: without ORDER BY, which rows you get as “the first five” can be engine-defined and unstable. If the sample must be meaningful (latest orders, highest amounts), sort first, then limit.

Some engines use FETCH FIRST or TOP instead of LIMIT. The underlying conceptual pattern remains completely identical. This series uses LIMIT because SQLite and many warehouses accept it or a close cousin.

LIMIT and pagination (concept)

Apps often combine LIMIT with an offset to page results. As a data analyst, you more often want better filters than deep offsets. If you need rows 10,000 through 10,020 of an arbitrary order, ask whether a keyset filter would be clearer. For now: LIMIT is for sampling and head checks, not for pretending you loaded the whole warehouse.

Putting it together

A clean extract pattern for a marketing list draft:

SELECT DISTINCT
  name AS customer_name,
  email AS email_address,
  region AS sales_region
FROM customers
WHERE email IS NOT NULL
ORDER BY customer_name
LIMIT 100;

What each piece does:

  • Explicit columns and aliases for a human-facing file.
  • DISTINCT in case your real table is messier than the toy.
  • A filter so you do not hand blank emails to a mail tool (missing value details expand in our filtering guide).
  • ORDER BY for a stable reading order.
  • LIMIT as a safety cap while you validate the logic.

Worked example: shape an ops glance

Question: “Show me a compact view of recent paid orders with friendly names.”

SELECT
  order_id AS id,
  customer_id AS customer,
  order_date AS ordered_on,
  amount AS order_amount,
  status AS order_status
FROM orders
WHERE status = 'paid'
ORDER BY order_date DESC
LIMIT 10;

You still do not need every column that might exist on a real orders table (shipping details stored as JSON, a text format for nested data, fraud scores, raw payloads). You only need the primary glance fields. When you later join customer names in our joins guide, you will add name AS customer_name the same way.

Second task: unique paid customers by id from the orders table alone.

SELECT DISTINCT
  customer_id
FROM orders
WHERE status = 'paid'
ORDER BY customer_id;

That list is a set of ids, not a count. For calculating unique paying customers, our aggregates guide explains COUNT(DISTINCT customer_id) as the cleaner pattern. Notice how these foundational selection techniques set up later analysis.

Handoffs to spreadsheets, business intelligence, and Python

Intentional SELECT lists make every handoff kinder:

  • Sheets: fewer columns means less horizontal scrolling and fewer accidental sorts on the wrong field.
  • BI tools: aliases become friendlier field pickers when the tool reflects SQL names.
  • Python: reading a narrow result into pandas keeps memory and mental load down. See the Python series when you bridge tools.

If you still live mostly in grids, pair these habits with spreadsheets to data. For a map of site paths, use Learn.

Column lists as documentation

A shared query with an explicit SELECT list is a tiny contract. Anyone reading it can answer: what fields does this extract promise? That matters when you hand work to a contractor, schedule a job, or paste SQL into a ticket. SELECT * says “whatever the table happens to be this week,” which is a moving target.

In regulated or privacy-sensitive environments, explicit lists also reduce accidental leakage. You are less likely to ship a column that should never leave the warehouse if the column is not named in the query. That is not a substitute for proper access control. It is still a useful second line of defense for analysts who export samples.

DISTINCT, LIMIT, and honesty in sampling

When leadership asks for “a quick sample of customers,” decide whether they need random-ish examples, latest signups, highest value buyers, or unique emails. LIMIT 20 after ORDER BY date gives the latest. LIMIT 20 with no ORDER BY gives whatever the engine returns first, which is a poor story in a meeting. DISTINCT on email before LIMIT gives unique addresses, but it can hide that your table has duplicate person rows that still need a quality fix.

Write the sample intent in a comment in your notes:

SELECT
  customer_id,
  name,
  region
FROM customers
ORDER BY customer_id
LIMIT 10;
-- Intent: stable first ten ids for a screenshot, not a statistical sample

This returns the first ten customers by id, in the same order every time, because ORDER BY fixes the order before LIMIT cuts the list off. The comment records why you took those rows, so nobody later mistakes them for a fair sample.

Honesty about sampling prevents fake confidence. A LIMIT query is not a census.

Common mistakes

  • Shipping SELECT * to production jobs. Exploration leftover becomes a silent contract.
  • Alias typos that break downstream dashboards. Rename carefully when something is already in use.
  • DISTINCT as a panic button. If you need DISTINCT to make a join “look right,” you may have a join grain problem in multi-table queries.
  • LIMIT without ORDER BY when you believe you are looking at “the top” anything.
  • Assuming DISTINCT reorders rows. Add ORDER BY when order matters.
  • Selecting columns you do not understand. If you cannot explain a field, do not put it in a executive CSV.

Practice

  • Rewrite any SELECT * in your notes into an explicit list.
  • Create a customer extract with aliases suitable for a non-technical reader.
  • List DISTINCT statuses and DISTINCT regions. Compare row counts to the full tables.
  • Fetch the five highest amount orders using ORDER BY and LIMIT.
  • Write one query that uses a column list, an alias, DISTINCT or a clear reason you did not need it, and a LIMIT for safety while testing.

Quick recap

  • SELECT columns are a contract: ask only for what you need.
  • SELECT * is for private exploration, not default production style.
  • Aliases rename results for humans and downstream tools.
  • DISTINCT returns unique result rows; it does not fix root-cause duplication.
  • LIMIT samples; pair it with ORDER BY when “top” matters.
  • Clean select lists make Sheets, BI, and Python handoffs less painful.

Next: filters and sorting. WHERE, AND/OR, IN, BETWEEN, careful LIKE, ORDER BY, and NULL basics. That is the focus of our next tutorial.

FAQ: SELECT query craft

Does column order in SELECT matter?

For correctness of most queries, no. For human reading and CSV handoffs, yes. Put identifiers first, then descriptive fields, then measures. Keep the same order across related extracts so stakeholders can compare files without hunting columns.

Should I run SELECT DISTINCT by default?

No. Default to exact rows. Add DISTINCT when the question is about unique combinations, or when you have proven duplicates and understand why they exist. Habitual DISTINCT is a smell that the grain or join is wrong, especially once you reach multi-table joins.

Is LIMIT 1 a good way to check existence?

It can be a quick smoke test (“is there at least one paid order?”), but for existence in serious SQL you will later prefer patterns like EXISTS in our subqueries tutorial. For now, LIMIT 1 with a clear WHERE clause is acceptable in interactive exploration.


Your next step

Write one query against the practice orders table that lists the ten most recent orders, with the identifier first and the amount last. Then run the same query with DISTINCT and compare the row counts. If they differ, find the duplicate rows before you decide which version answers the question, because that habit prevents most double-counting mistakes.

Sources

References for SELECT statement behavior:

Written by

Jose S

Founder & Lead Analyst · Analytics Made Simple

Hands-on data strategist, analytics engineering lead, and educator. Writing practical, no-fluff guides to help everyday teams, analysts, and engineers master SQL, AI systems, and modern data architectures.

Keep going

Same lessons in your feed

Short diagrams, hooks, and weekly tutorials on Substack, Instagram, X, and Facebook.

Google Search Prefer our practical guides in Google Search & Top Stories: