Your database is full of orders, customers, and products, but every time you try to pull a report you get wrong data or a page that never finishes loading? Or you’ve just inherited an old ERP and need to extract revenue by category, but have no idea where to start? We’ve been there. We’re Meteora Web — we’ve been working with businesses since 2017, from domain to revenue, as a single point of contact. When we talk SQL, we start from real problems, not database theory.
This guide cuts straight to the point: SELECT, FROM, WHERE, JOIN, and GROUP BY. Master these five statements and you’ll get 90% of the answers any business asks from its database. Let’s walk through them with real-world examples — the kind you face every day in an e-commerce store or a management system.
1. SELECT and FROM — The basics most people misuse
Concrete case: you have an orders table. You want all orders from last month. You write:
SELECT * FROM orders WHERE order_date >= '2025-01-01';It works, but SELECT * is almost always a mistake. On a real database with dozens of columns and thousands of rows, pulling everything slows the network, fills memory, and risks exposing sensitive data (e.g., costs, password hashes). We’ve seen clients suffer 30-second queries just because they selected 10 unnecessary columns.
Best practice: select only the columns you actually need. For a sales report, you need order_id, date, total, customer_id. Nothing more.
SELECT order_id, order_date, total, customer_id
FROM orders
WHERE order_date >= '2025-01-01';If you need to rename a column for clarity, use an alias (AS).
SELECT total AS total_amount, customer_id AS client_id
FROM orders;It’s a habit that pays off: cleaner reports, faster queries, fewer errors.
Actionable step
Take one of your most-used tables and rewrite a query replacing SELECT * with the actual column names you need. Note the execution time difference if you have enough data.
2. WHERE — Filtering with precision (and without mistakes)
WHERE is your sieve. But it’s easy to get it wrong. The most common mistake? Forgetting quotes for strings or using = instead of LIKE for partial matches.
Real scenario: find all orders from a customer named "Mario Rossi". But in the database the name might be "Rossi Mario" or just "Mario". WHERE name = 'Mario Rossi' won’t cut it.
SELECT * FROM customers WHERE first_name LIKE '%Mario%' AND last_name LIKE '%Rossi%';% is a wildcard — it means "any characters before or after". Warning: LIKE '%Mario%' is slower on large tables because it can’t use indexes. If this search is frequent, consider a full-text index or a normalized column.
Another common error: filtering dates as strings. Use the standard YYYY-MM-DD format and compare with >=, <.
SELECT * FROM orders
WHERE order_date BETWEEN '2025-01-01' AND '2025-01-31';Important: if the column includes time, BETWEEN includes the end date up to midnight. For exact intervals, use >= and <.
Actionable step
- Check any of your queries that use
WHERE field = 'value'on strings that might have spaces or case mismatches. AddTRIM()andLOWER()if needed. - Verify date conditions: are they compared as strings or actual dates? If strings, convert with
DATE().
3. JOIN — When data lives in different tables
An e-commerce database doesn’t stuff everything into one table. You have orders, customers, products, order_items. To get a report like "customer name, order total, purchased products" you need to join tables.
The most used join is INNER JOIN: returns only rows that have matches in both tables.
SELECT c.name, o.order_id, o.total
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id
WHERE o.order_date >= '2025-01-01';Watch out for aliases: use short letters (c, o) but keep them clear. With 5 joins it can get messy.
LEFT JOIN is the second most common: returns all rows from the left table, and if no match exists, fills with NULL. Example: list all customers with their last order (including those who never ordered).
SELECT c.name, MAX(o.order_date) AS last_order
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_id;We at Meteora Web have seen typical mistakes: forgetting the join condition (writing FROM customers, orders without ON creates an explosive Cartesian product). Or using LEFT JOIN when INNER JOIN is needed (and vice versa). Rule: if you want only customers with orders, use INNER; if you want all customers, use LEFT.
Actionable step
Take your most complex query with joins and check: every JOIN has its ON? Did you forget a LEFT? Try removing one join at a time to see what happens to the result.
4. GROUP BY — Aggregating without losing control
GROUP BY is for summarizing: total sales per month, number of orders per customer, average spend per category. But it’s also where the sneakiest errors hide.
Classic mistake: putting a column in SELECT that is neither aggregated nor grouped. Many databases (like MySQL with sql_mode=ONLY_FULL_GROUP_BY disabled) allow it, but the result is unpredictable — it picks a random value.
Correct example: we want total orders and order count per customer.
SELECT customer_id, COUNT(*) AS order_count, SUM(total) AS total_spent
FROM orders
GROUP BY customer_id;If you want to filter after aggregation (e.g., only customers who spent more than €1000), use HAVING, not WHERE.
SELECT customer_id, SUM(total) AS total_spent
FROM orders
GROUP BY customer_id
HAVING total_spent > 1000;Critical difference: WHERE filters rows before GROUP BY, HAVING after. If you use WHERE with an aggregate function, the database will throw an error.
Actionable step
- Review your queries with
GROUP BY: are all non‑aggregated columns listed in theGROUP BY? If not, rewrite them. - Try converting a
WHEREthat filters on a sum into a properHAVINGclause.
5. Putting it all together — A real e-commerce example
Imagine a clothing store (like the one we managed from inside with its ERP). You want, for each product category, how many pieces were sold and the gross revenue in January 2025, but only for customers who spent at least €50 in total.
SELECT cat.category_name,
COUNT(oi.line_id) AS pieces_sold,
SUM(oi.price * oi.quantity) AS revenue
FROM order_items oi
JOIN products p ON oi.product_id = p.product_id
JOIN categories cat ON p.category_id = cat.category_id
JOIN orders o ON oi.order_id = o.order_id
JOIN customers c ON o.customer_id = c.customer_id
WHERE o.order_date BETWEEN '2025-01-01' AND '2025-01-31'
AND (SELECT SUM(o2.total) FROM orders o2 WHERE o2.customer_id = c.customer_id) >= 50
GROUP BY cat.category_id, cat.category_name
ORDER BY revenue DESC;This query uses JOIN across 5 tables, WHERE with a subquery, GROUP BY and ORDER BY. It’s a real example you can adapt to your own database.
Performance note: the subquery in WHERE runs once per row of the join. On millions of rows it might be slow. A better solution is to precompute customer totals using a CTE or temporary table — but that’s for a more advanced guide.
In summary — what to do now
- Rewrite your
SELECT *into targeted selections. Pick a frequent report query and replace*with the columns you actually need. Measure the time before and after. - Check your
WHEREclauses: are strings quoted? Are dates compared as dates? UseBETWEENor>=and<for precise intervals. - Review every
JOINin your application. Does each join have a key relationship? Are you usingINNERorLEFTas needed? Make sure there are no accidental Cartesian products. - Audit each
GROUP BY: are all non‑aggregated columns in the SELECT included in the GROUP BY? If you need filters on aggregates, useHAVINGinstead ofWHERE. - Practice with a sample database. Download MySQL’s
employeessample database or use one of your own exports. Write 5 queries combining SELECT, FROM, WHERE, JOIN, GROUP BY with HAVING and ORDER BY. The more you write, the more it sticks.
We at Meteora Web work with companies that often discover these basics only when the site crashes or a report fails. If you want to avoid that, start today. Your database is the heart of your business — treat it with respect.
Sponsored Protocol