, ,

SQL SELECT, WHERE and ORDER BY: Your First Queries (Data Analyst Series, Part 6)

Write your first real SQL queries. SELECT to choose columns, WHERE to filter rows, ORDER BY to sort and LIMIT to cap, all against a five-row sample table.

Data Analyst Series · Part 6 of 22

Your customers table has 40,000 rows. Your manager wants the customers who signed up in Pune during June, newest first, and she wants it before the 10am call. Scrolling a spreadsheet will not find them, and a pivot table cannot really answer it either. This is the exact job SQL was built for, and three keywords do almost all of it: SELECT to choose the columns, WHERE to keep only the rows you want, and ORDER BY to sort what is left.

Part 5 gave you the mental model, that a database keeps data in typed tables, one record per row and one field per column. Now you talk to those tables. A query is a plain request you hand the database: from this table, give me these columns, for the rows that match this rule, in this order. Learn the shape of that one sentence and you can already answer most of the everyday questions a junior analyst gets. Joins and grouping, coming in Parts 7 and 8, both hang off this same frame.

Key takeaways

SELECT names the columns you want, FROM names the table. Listing columns beats SELECT star for anything you will run more than once.

WHERE keeps only the rows that make its condition true. You combine conditions with AND and OR, and you match text, ranges and lists with LIKE, BETWEEN and IN.

ORDER BY sorts the result, ascending by default, and LIMIT caps how many rows come back. A LIMIT without an ORDER BY returns an unpredictable slice.

Who this is for: You have read Part 5 and can picture a table as typed rows and columns. You have never written a query, or you have copied one without really reading it. You do not need anything installed to follow along; a free browser sandbox like the ones at pgexercises or db-fiddle will run every query here. This part uses PostgreSQL, the open-source database this series writes SQL against, currently at version 18.

What a SELECT actually asks for

Every query in this part reads from two small tables. A customers table holds one row per customer, with a customer_id, a name, a city, a signup_date and an is_active flag. An orders table holds one row per order. Here is the customers data the examples run against, so you can check each result by eye.

customer_idnamecitysignup_dateis_active
1001Ana RuizPune2026-06-14true
1002Sam LeeLucknow2026-02-03true
1003Priya NairPune2026-06-28false
1004Diego AlonsoDelhi2026-05-19true
1005Mei ChenMumbai2026-06-02true

The sample customers table. Five rows is enough to see exactly what each query keeps and drops.

The smallest useful query names the columns you want and the table they live in. SELECT lists the columns, separated by commas, and FROM names the table. Read it as a sentence: select name and city from customers.

SELECT name, city
FROM customers;

Result: two columns, five rows, one row per customer. name and city for Ana, Sam, Priya, Diego and Mei.

Common failure: forgetting the semicolon. Most tools need it to know the statement has ended, and a query that just hangs is often a missing semicolon, not a broken database.

The four moving parts of a queryEach clause has one job. Read them left to right like a sentence.SELECT name, citychoose columnsFROM customerswhich tableWHERE city = Punekeep matching rowsORDER BY date DESCsort the resultYou write them in this order every time. SELECT, FROM, then optional WHERE and ORDER BY.The database, though, does not run them in the order you wrote. More on that below.
A full query is just four clauses, each with a single job. Two of them, WHERE and ORDER BY, are optional.

Name your columns, skip the star

You will see SELECT star everywhere, written with an asterisk. It means give me every column. It is fine for a quick look when you are exploring a table you do not know yet, and it is a poor habit for anything you save or schedule. Naming the columns you actually need makes the query say what it means, moves less data across the network, and does not silently break when someone adds a wide new column to the table next quarter. Type the extra words. Future-you, reading the query in six months, will know exactly what it returns.

You can also rename a column in the output with AS, which is called an alias. It does not change the table, only the label on the result, which is handy when a column name is cryptic or when you want a cleaner heading for a report.

SELECT name AS customer_name,
       signup_date AS joined_on
FROM customers;

Result: the same rows, but the headings now read customer_name and joined_on. The data is untouched, only the labels change.

Remove duplicates with SELECT DISTINCT

Sometimes you do not want every row, you want every distinct value. A quick question like which cities do we have customers in should return each city once, not once per customer. Put DISTINCT right after SELECT and the database collapses repeats for you. It looks at the whole selected row, so DISTINCT city returns each city a single time, while DISTINCT city, is_active returns each unique pairing of the two.

This is the fastest way to audit a column before you trust it. Run DISTINCT on a city column and you often find Pune, pune and Pune with a trailing space all sitting there as separate values, which is exactly the messy-data problem Part 9 will teach you to clean. Seeing the distinct list is how you catch it early.

SELECT DISTINCT city
FROM customers
ORDER BY city;

Result: four rows, Delhi, Lucknow, Mumbai, Pune. Pune appears once even though two customers live there.

Common failure: expecting DISTINCT to dedupe just one column while showing others. It cannot; it dedupes the entire selected row, so adding more columns usually brings the duplicates back.

Keep only the rows you want with WHERE

WHERE is the workhorse. It sits after FROM and holds a condition, a test that is either true or false for each row. The database keeps the rows where the test is true and drops the rest. The condition uses the comparison operators you already know from arithmetic, plus a few words that read like English.

Text values go in single quotes, numbers do not. That single rule trips up more beginners than anything else. A city is text, so it is ‘Pune’ with quotes; an amount is a number, so it is 500 with none. Getting this backwards is the most common error you will hit in your first week.

SELECT name, city, signup_date
FROM customers
WHERE city = 'Pune';

Result: two rows, Ana Ruiz and Priya Nair, the only customers whose city is Pune.

Common failure: writing WHERE city = Pune without the quotes. The database reads Pune as a column name, cannot find one, and errors. Quote your text.

Real questions need more than one test. AND keeps a row only when both conditions are true; OR keeps it when either is true. Answering the manager from the opening now takes shape: customers in Pune, who signed up in June. June is a date range, so you test the signup_date against the first and last of the month.

SELECT name, signup_date
FROM customers
WHERE city = 'Pune'
  AND signup_date >= '2026-06-01'
  AND signup_date <= '2026-06-30';

Result: Ana Ruiz (2026-06-14) and Priya Nair (2026-06-28). Diego signed up in May and drops out, even though the rest of his row would qualify.

Worked example

Mixing AND and OR without parentheses is a classic trap. WHERE city = ‘Pune’ OR city = ‘Delhi’ AND is_active = true does not mean what it looks like, because AND binds tighter than OR, so the database reads it as Pune, OR (Delhi that is active).

Say what you mean with brackets: WHERE (city = ‘Pune’ OR city = ‘Delhi’) AND is_active = true. Now both cities must also be active. When AND and OR appear together, parenthesise, every time.

Matching text, ranges and lists

Equals is fine when you know the exact value, but often you do not. Four extra operators cover most of what is left, and each reads close to plain English. LIKE matches a text pattern, where the percent sign stands for any run of characters and the underscore stands for exactly one. BETWEEN tests a range and includes both ends. IN checks membership in a list, a tidy replacement for a pile of OR tests. IS NULL checks for a missing value, which is its own thing and not the same as an empty string or a zero.

OperatorTests forExample conditionMatches
=Exact matchcity = ‘Pune’Pune only
<>Not equalcity <> ‘Pune’every city except Pune
LIKEText pattern, case sensitivename LIKE ‘A%’names starting with A
ILIKEPattern, case insensitivename ILIKE ‘a%’A or a to start
BETWEENRange, ends includedamount BETWEEN 500 AND 2000500 up to 2000
INMembership in a listcity IN (‘Pune’,’Delhi’)either city
IS NULLMissing valuesignup_date IS NULLrows with no date

The WHERE operators you reach for daily. ILIKE is a PostgreSQL convenience; on other databases you lower-case both sides instead.

IN deserves a moment because it saves so much typing. These two conditions return the same rows, and the second is far easier to read and to extend.

-- verbose
WHERE city = 'Pune' OR city = 'Delhi' OR city = 'Mumbai'

-- same result, cleaner
WHERE city IN ('Pune', 'Delhi', 'Mumbai');

Result: rows for Ana, Priya, Diego and Mei. Only Sam, in Lucknow, drops out.

Gotcha: NULL means unknown, so it refuses ordinary comparison. WHERE signup_date = NULL returns nothing, not the rows you expect, because a value cannot equal unknown. Always test a missing value with IS NULL, and its opposite with IS NOT NULL. This one bites nearly every beginner once, so learn it before it costs you a wrong report.

Here is a detail that clears up a lot of confusion later. You write SELECT first, but the database does not run it first. It starts at FROM to find the table, applies WHERE to throw out rows, then works out SELECT, then ORDER BY, and finally LIMIT. That is why you cannot filter on a column alias you invented in SELECT: at the moment WHERE runs, that alias does not exist yet.

flowchart LR
  A[FROM customers] --> B[WHERE keep matching rows]
  B --> C[SELECT chosen columns]
  C --> D[ORDER BY sort]
  D --> E[LIMIT cap the count]
The order the database runs the clauses, which is not the order you type them. FROM and WHERE come first, SELECT later.

Sort the result with ORDER BY

A result comes back in whatever order the database finds convenient, which is to say no reliable order at all. ORDER BY fixes that. Name a column and the rows sort by it, ascending by default, meaning smallest to largest for numbers, earliest to latest for dates, and A to Z for text. Add DESC for the reverse. The manager wanted newest first, which is signup_date descending.

SELECT name, city, signup_date
FROM customers
WHERE city = 'Pune'
ORDER BY signup_date DESC;

Result: Priya Nair (2026-06-28) first, then Ana Ruiz (2026-06-14). Same two rows as before, now newest at the top.

You can sort by more than one column. List them in priority order and the second column only breaks ties inside the first. Sorting by city ascending, then signup_date descending, groups the cities alphabetically and, within each city, puts the newest signup on top. Each column can have its own ASC or DESC.

SELECT name, city, signup_date
FROM customers
ORDER BY city ASC, signup_date DESC;

Result order: Delhi (Diego), Lucknow (Sam), Mumbai (Mei), then Pune with Priya above Ana because her signup is later.

Cap the rows with LIMIT

LIMIT caps how many rows come back. It goes last and takes a number. Asking for the ten newest signups is an ORDER BY to get newest first, then a LIMIT to keep the top ten. On a 40,000 row table this is also how you peek at data without dragging every row into your tool.

SELECT name, signup_date
FROM customers
ORDER BY signup_date DESC
LIMIT 3;

Result: the three newest signups, Priya (06-28), Ana (06-14), Mei (06-02), in that order.

Common failure: LIMIT with no ORDER BY. The database is then free to hand you any three rows, and it may hand you different ones next time. If LIMIT is meant to pick the top of something, it needs an ORDER BY to define the top.

Signups by city in the wider sampleRow counts a WHERE on city would return, per city0100200300320Pune260Delhi190Mumbai145LucknowWHERE city = Pune would return the tallest bar. ORDER BY then decides which of those rows sit on top.
How many rows each city filter keeps in the fuller dataset. WHERE picks the bar, ORDER BY and LIMIT decide which rows within it you see.

My take

When a query gets long, I stop thinking about SELECT and start with WHERE. The columns are easy; the rows are where the mistakes and the money are. Write the filter first, run it, eyeball the row count against what you expect, and only then decide which columns to show. Nine times out of ten a wrong number in a report traces back to a WHERE that kept too much or too little, not to the SELECT.

Build the query one clause at a time

The habit that will serve you longest is to build a query in layers rather than all at once. Start with SELECT and FROM and run it to see the raw rows. Add the WHERE and run again to confirm the filter drops what it should. Add ORDER BY, then LIMIT, checking the result at each step. When something looks wrong, you know it was the clause you just added, so you are never staring at a five-line query wondering which part lied to you. This is slower for the first day and much faster for every day after.

You can now answer the opening question in full: SELECT the name and signup_date, FROM customers, WHERE the city is Pune and the signup_date falls in June, ORDER BY signup_date descending. That is a real analyst task, done in four lines, and you understand every one of them. Part 7 takes the next step, pulling columns from two tables at once by joining them on a key, which is how you attach each order to the customer who placed it. If any operator here felt slippery, run it against the five-row sample until the result stops surprising you, and keep the Data Analyst guide open as your map. Reach back to Part 5 if the table and key vocabulary needs a refresher.

This week, open a free SQL sandbox in your browser, type out the five queries above by hand rather than copying them, and change one value in each to see what moves. Typing SQL yourself, wrong then right, is how the syntax stops being something you look up and starts being something you know.

Data Analyst Series · Part 6 of 22
« Previous: Part 5  |  Guide  |  Next: Part 7 »

References

About The Author


Discover more from Journal of Intelligent Infrastructure – By Dr Pranay Jha

Subscribe to get the latest posts sent to your email.

Leave a Reply

Your email address will not be published. Required fields are marked *

Architect’s Toolkit

About the Author

Dr. Pranay Jha is a Cloud and AI Consultant with 18+ years of experience in hybrid cloud, virtualization, and enterprise infrastructure transformation. He specializes in VMware technologies, multi-cloud strategy, and Generative AI solutions. He holds a PhD in Computer Applications with research focused on Cloud and AI, has published multiple research papers, and has been a VMware vExpert since 2016 and a VMUG Community Leader.

Discover more from Journal of Intelligent Infrastructure - By Dr Pranay Jha

Subscribe now to keep reading and get access to the full archive.

Continue reading