Business Analysis × MySQL
Created for learning MySQL in an easy and simple way by Shouryaa Sharma

Learn SQL.
Think like an analyst.

A beginner-first, deeply explained MySQL learning platform. Every keyword is explained, every example has a business purpose, and every chapter ends with a scenario-based Level Up Quiz.

7 chapters Line-by-line SQL Business scenarios Interactive quizzes
01

Introduction & Setup

Understand where data lives and how you communicate with it.

What is a database?

A database is an organized collection of information stored so that people and applications can find, change, and analyze it. A database is not just a file. It is managed by database software that applies rules, controls access, protects data, and answers questions.

What A structured place where business facts are stored.
Why To prevent duplicate, inconsistent, and lost information.
Under the hood MySQL stores records on disk and uses memory, indexes, and an optimizer.
BA scenario A sales BA queries customers and orders to produce a revenue report.

What does RDBMS mean?

RDBMS means Relational Database Management System. “Relational” means data is organized into related tables. For example, a Customers table can be related to an Orders table through a customer ID. “Management System” means software manages storage, security, queries, relationships, and data rules for you.

Real-life scenario: Imagine an online store. Customers, products, orders, and payments are different business entities. Storing everything in one giant spreadsheet would create repeated customer names and difficult updates. MySQL stores each entity separately and connects them reliably.

Why MySQL?

MySQL is a popular open-source relational database system. It understands SQL, the language used to communicate with relational databases. It is widely used in websites, reporting systems, data warehouses, applications, and business intelligence pipelines.

How it works: Your SQL text is sent to the MySQL server. The server parses the statement, checks whether you have permission, creates an execution plan, reads or changes the required data, and returns a result.
BA use: You may use MySQL to validate whether a dashboard number is correct, investigate a process failure, or create a dataset for a stakeholder.

Server, Workbench, and command line

The MySQL Server stores data and runs SQL. MySQL Workbench is a graphical tool that helps you connect, draw models, and execute queries. The command-line client is a text-based tool that is useful for automation and professional troubleshooting.

SQL / Terminal
# Connect to a local MySQL server
mysql -u analyst -p -h 127.0.0.1 -P 3306
Part Meaning
mysql Starts the MySQL command-line client.
-u analyst Supplies the username named analyst.
-p Asks MySQL to securely prompt for a password.
-h 127.0.0.1 Connects to the MySQL server on this computer.
-P 3306 Uses port 3306, the common MySQL port.

Connecting to and selecting a database

Connecting to a server identifies you. Selecting a database tells MySQL which collection of tables you want to work with during the current session. These are two separate ideas.

SQL
SHOW DATABASES;

CREATE DATABASE IF NOT EXISTS sales_lab;

USE sales_lab;

SELECT DATABASE();

Line-by-line breakdown

Token Explanation
SHOW A command that asks MySQL to display information.
DATABASES The information we want to display: available databases.
CREATE DATABASE Creates a new database container.
IF NOT EXISTS Prevents an error if the database already exists.
sales_lab The business-friendly name of the database.
USE Changes the active database for the current session.
DATABASE() Returns the database currently selected by the session.
; Marks the end of a SQL statement.
Why the semicolon? The server may receive many lines through a client. The semicolon tells the client that one complete statement has finished and can be sent for execution.

Installation and service commands

MySQL has two important pieces: the server, which stores and processes data, and the client, such as Workbench or the terminal, which sends SQL to that server. Install the server first; Workbench is optional but helpful for a beginner because it shows databases, tables, and results visually.

Windows

  1. Install MySQL Community Server using MySQL Installer.
  2. Choose the Developer Default option if you want Server and Workbench together.
  3. Keep port 3306, create a root password, and remember it securely.
  4. Start MySQL Workbench and create a connection to 127.0.0.1.

macOS terminal installation

Terminal
# Install MySQL with Homebrew
brew install mysql

# Start the MySQL server as a background service
brew services start mysql

# Stop the service when you intentionally need to
brew services stop mysql

# Check that the command-line client is available
mysql --version

Ubuntu / Debian Linux

Terminal
sudo apt update
sudo apt install mysql-server
sudo systemctl enable --now mysql
sudo systemctl status mysql
CommandWhat it doesWhen a BA needs it
mysql --versionPrints the installed client version.Confirm installation and diagnose version-specific behavior.
systemctl status mysqlShows whether the Linux server is running.Check why a local connection cannot be made.
systemctl start mysqlStarts the server service.Recover after the service was stopped.
systemctl stop mysqlStops the server service.Maintenance only; existing connections will fail.
Beginner check: if Workbench says “Can’t connect to MySQL server,” check the server service first, then verify host, port, username, and password. The error does not usually mean your SQL query is wrong.

⚡ Level Up Quiz: Introduction & Setup

1. A BA needs to query customer and order data. What is MySQL?
2. Which command selects the database used by the current session?
3. What does port 3306 usually identify?
4. A Workbench connection succeeds, but a query says “No database selected.” What should you do?
02

Database & Table Architecture

Turn business entities into reliable data structures.

Creating a database

A database is a container for tables, views, stored procedures, and permissions. Businesses often separate databases by environment or business domain, such as sales, HR, inventory, development, and production.

SQL
CREATE DATABASE IF NOT EXISTS sales_db
CHARACTER SET utf8mb4
COLLATE utf8mb4_0900_ai_ci;
Token Beginner explanation
CREATE Tells MySQL that a new object must be created.
DATABASE Identifies the type of object being created.
IF NOT EXISTS Makes the script safe to run again if the database already exists.
sales_db The name of the new database.
CHARACTER SET utf8mb4 Allows the database to store a wide range of human languages and symbols.
COLLATE Defines how text is compared and sorted.
; Ends the statement.
Real-life scenario: A company separates development and production databases so a BA can test a report without accidentally changing live customer data.

Creating a table from a business entity

A table represents one type of business object or event. Before creating it, define its grain. Grain means what one row represents. In a Customers table, one row should represent one customer. In an Orders table, one row should represent one order.

SQL
CREATE TABLE customers (
  customer_id INT PRIMARY KEY AUTO_INCREMENT,
  full_name VARCHAR(100) NOT NULL,
  email VARCHAR(255) UNIQUE,
  signup_date DATE NOT NULL
);
Token Explanation
CREATE TABLE Tells MySQL to create a new table definition.
customers The name of the table representing the customer entity.
( and ) Open and close the list of column definitions.
customer_id The column that identifies one customer.
INT Stores whole numbers such as 1, 2, and 300.
PRIMARY KEY Requires every row to have a unique identity.
AUTO_INCREMENT Automatically generates the next numeric ID.
VARCHAR(100) Stores variable-length text up to 100 characters.
NOT NULL Requires a value; the column cannot be empty as NULL.
UNIQUE Prevents two rows from having the same email value.
, Separates one column definition from the next.
Under the hood: MySQL stores the table definition in its metadata dictionary. It knows each column's type, nullability, indexes, and constraints before any business data is inserted.

Data types: accuracy and performance

A data type tells MySQL what kind of value belongs in a column. Correct types improve validation, storage, sorting, calculations, and index performance.

Type Use it for
INT Whole numbers, quantities, IDs, and counts.
VARCHAR(n) Variable-length text such as names and emails.
DATE A calendar date when the time is not required.
DECIMAL(12,2) Exact money and financial measurements.
BA scenario: If revenue is stored as VARCHAR, a SUM may fail or convert values unpredictably. If it is stored as DECIMAL, financial totals are exact.

Primary keys and foreign keys

A primary key identifies a row. A foreign key creates a relationship between a child table and a parent table. This lets MySQL stop invalid relationships, such as an order pointing to a customer who does not exist.

SQL
CREATE TABLE orders (
  order_id INT PRIMARY KEY AUTO_INCREMENT,
  customer_id INT NOT NULL,
  order_date DATE NOT NULL,
  total_amount DECIMAL(12, 2) NOT NULL,
  FOREIGN KEY (customer_id)
    REFERENCES customers(customer_id)
);
Token Explanation
order_id Unique identifier for an order.
customer_id Stores which customer owns the order.
FOREIGN KEY Declares that a column points to a key in another table.
REFERENCES Identifies the parent table and parent column.
customers(customer_id) The valid parent identity that the order must reference.

⚡ Level Up Quiz: Database Architecture

1. A customer can place many orders. Which design is appropriate?
2. Which type is best for exact financial amounts?
3. What does NOT NULL mean?
4. An order refers to customer_id 999, but that customer does not exist. Which rule should prevent the row?
03

CRUD Operations

Create, Read, Update, and Delete data safely.

INSERT: adding data

INSERT creates a new row. You provide values for selected columns. Explicitly listing the columns is safer than depending on the table's physical column order.

SQL
INSERT INTO customers
  (full_name, email, signup_date)
VALUES
  ('Maya Singh',
   'maya@example.com',
   '2026-01-14');
Token Explanation
INSERT Tells MySQL that a new row will be created.
INTO Introduces the table receiving the new data.
customers The destination table.
(full_name, email, signup_date) The destination columns, in the order values will be supplied.
VALUES Introduces the actual values to store.
'Maya Singh' Text value inserted into full_name.
, Separates values and columns.
; Ends the INSERT statement.
Real-life scenario: A CRM integration receives a new customer registration. The integration uses INSERT to save the customer before an order can be connected to them.

SELECT: reading data

SELECT asks MySQL to return data. It does not modify the table. Think of it as asking a precise question: “Which columns do I want, and from which table?”

SQL
SELECT customer_id, full_name, email
FROM customers;
Token Explanation
SELECT The command that tells MySQL to read and return data.
customer_id The first column we want to see.
full_name The second column we want to see.
email The third column we want to see.
, Separates selected columns.
FROM Points to the source table.
customers The table holding the requested information.
; Signals the end of the query.
Why avoid SELECT *? The asterisk means “all columns.” It is convenient for exploration, but explicit columns make reports easier to understand, reduce data transfer, and protect the output when a new column is added.

UPDATE: changing existing data

UPDATE changes values in existing rows. The WHERE clause is the safety boundary. It identifies exactly which rows should change.

SQL
UPDATE customers
SET email = 'new@example.com'
WHERE customer_id = 42;
Token Explanation
UPDATE Tells MySQL existing rows will be changed.
customers The table containing the rows to change.
SET Introduces the new value assignment.
email The column that will be changed.
= Assigns the value on the right to the column on the left.
WHERE Limits the update to rows matching a condition.
customer_id = 42 Only the customer whose ID is 42 should change.
Critical danger: If you remove the WHERE clause, MySQL can change every customer's email. A safe workflow is: first run SELECT with the same WHERE, confirm the rows, then run UPDATE. In production, use a transaction and review the affected-row count.

DELETE and TRUNCATE

DELETE removes rows. It can use WHERE to target a specific population. TRUNCATE removes every row from a table and is commonly used for temporary staging tables.

SQL
DELETE FROM customers
WHERE customer_id = 42;

TRUNCATE TABLE staging_customers;
Token Explanation
DELETE FROM Begins a row-removal operation on the named table.
customers The table from which a row will be removed.
WHERE customer_id = 42 Limits removal to one customer.
TRUNCATE TABLE Removes all rows from the entire table.
staging_customers A temporary or reloadable table in this example.
Difference: DELETE is row-oriented and can filter. TRUNCATE is a table-level operation and cannot use WHERE. Never use TRUNCATE on production data unless the complete removal is intentional and approved.

⚡ Level Up Quiz: CRUD

1. A CRM receives a new customer registration. Which command adds the row?
2. What is the danger of UPDATE without WHERE?
3. Which command can remove selected rows using WHERE?
4. You need to correct one customer's email. What is the safest first step?
04

Filtering & Sorting

Convert a vague question into an exact business population.

WHERE: filtering rows

WHERE decides which source rows qualify. It is how you express business conditions such as “paid orders,” “customers from the East region,” or “transactions greater than $500.”

SQL
SELECT order_id, total_amount
FROM orders
WHERE status = 'Paid'
  AND total_amount > 500;
Token Explanation
SELECT Requests the output columns.
FROM orders Uses the orders table as the source.
WHERE Begins the row-filtering condition.
status = 'Paid' Keeps only rows whose status exactly equals Paid.
AND Requires the next condition to also be true.
total_amount > 500 Keeps only orders larger than 500.

Operators with examples

Operator Example Meaning
= status = 'Paid' Exactly equal.
> amount > 100 Greater than.
< amount < 100 Less than.
AND paid AND amount > 100 Both conditions must be true.
OR region='East' OR region='West' At least one condition must be true.
IN region IN ('East','West') Matches any item in a list.
BETWEEN amount BETWEEN 100 AND 500 Checks an inclusive range.
LIKE name LIKE 'A%' Pattern matching; % means any characters.
IS NULL email IS NULL Finds missing or unknown values.

NULL is not an empty string

NULL means “unknown,” “not supplied,” or “not applicable.” It is not the same as zero, an empty string, or the word “NULL.” Because NULL represents unknown information, normal equality comparisons do not work.

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

-- Incorrect for missing values
WHERE email = NULL;
BA scenario: Find customers whose email is missing before launching a marketing campaign.

ORDER BY: sorting a report

SQL tables do not automatically have a meaningful order. ORDER BY tells MySQL how to arrange the returned rows. Sorting matters because a report usually needs a ranking, newest records first, or a chronological story.

SQL
SELECT order_id, total_amount
FROM orders
ORDER BY total_amount DESC;
Token Meaning
ORDER BY Begins the sorting instruction.
total_amount The column used for sorting.
DESC Descending order: largest value first.

LIMIT: control the result size

LIMIT restricts how many rows MySQL returns. It is valuable for quick checks, previews, top-N reports, and protecting yourself from displaying millions of rows during exploration.

SQL
SELECT order_id, total_amount
FROM orders
ORDER BY total_amount DESC
LIMIT 10;
Token Meaning
LIMIT Tells MySQL to cap the number of returned rows.
10 Returns no more than ten rows.

⚡ Level Up Quiz: Filtering & Sorting

1. A stakeholder asks for customers with no email address. Which condition is correct?
2. Which operator finds values from a list of regions?
3. Which clause creates a top-ten report after sorting?
4. A report needs paid orders from either East or West, but not other regions. Which pattern is clearest?
05

Advanced Data Analysis

Summarize data and combine business entities.

Aggregate functions

Aggregate functions take many input rows and produce a summary value. They are useful when the business question asks “how many,” “how much,” “what is the average,” “what is the smallest,” or “what is the largest.”

Function Meaning BA question
COUNT() Counts rows or non-null values. How many orders were placed?
SUM() Adds numeric values. What was total revenue?
AVG() Calculates the arithmetic average. What was average order value?
MIN() Finds the smallest value. What was the smallest order?
MAX() Finds the largest value. What was the largest order?
SQL
SELECT
  COUNT(*) AS order_count,
  SUM(total_amount) AS revenue,
  AVG(total_amount) AS average_order,
  MIN(total_amount) AS smallest_order,
  MAX(total_amount) AS largest_order
FROM orders;
Line Explanation
SELECT Begins the request for a calculated result.
COUNT(*) Counts all rows, including rows with NULL in individual columns.
SUM(total_amount) Adds all total_amount values.
AVG(total_amount) Divides the total by the number of non-null amount values.
MIN / MAX Find the smallest and largest amount.
AS Creates a readable output name called an alias.
FROM orders Identifies the table being summarized.

GROUP BY: one summary per group

GROUP BY divides rows into groups. If you group by region, MySQL creates one group for East, one for West, and so on. Then aggregate functions calculate values inside each group.

SQL
SELECT
  region,
  SUM(total_amount) AS regional_revenue
FROM orders
GROUP BY region;
Token Meaning
region The dimension used to create groups.
SUM(total_amount) Calculates revenue inside each region.
GROUP BY region Returns one output row for each region.
Important rule: If a selected column is not inside an aggregate function, it normally must appear in GROUP BY. Otherwise MySQL would not know which value to display for that group.

HAVING: filtering groups

WHERE filters individual rows before grouping. HAVING filters groups after the aggregate calculation. You cannot use WHERE to filter SUM(total_amount), because SUM does not exist until after rows are grouped.

SQL
SELECT region,
  SUM(total_amount) AS revenue
FROM orders
GROUP BY region
HAVING SUM(total_amount) > 100000;
Line Explanation
SELECT region Displays the group name.
SUM(total_amount) Calculates each region's total.
FROM orders Uses order rows as the source.
GROUP BY region Creates one group per region.
HAVING Filters the completed groups.
> 100000 Keeps only regions whose calculated revenue is above 100,000.

JOINs: connecting tables

A JOIN combines rows from two tables using a related column. Customers contain customer details; Orders contain transactions. The customer ID allows a query to bring the two business perspectives together.

SQL
SELECT
  c.full_name,
  o.order_id,
  o.total_amount
FROM customers AS c
INNER JOIN orders AS o
  ON o.customer_id = c.customer_id;
Token Meaning
customers AS c Gives the customers table the short alias c.
orders AS o Gives the orders table the short alias o.
INNER JOIN Returns only customers that have matching orders.
ON Introduces the matching rule.
o.customer_id = c.customer_id Connects an order to its customer.
INNER JOIN Only matching rows from both tables.
LEFT JOIN Every left-table row, even without a match.
RIGHT JOIN Every right-table row, even without a match.
BA use Find customers with orders or customers without orders.
Example: Use LEFT JOIN when management asks for customers who have never placed an order. The unmatched order columns will be NULL.

Subqueries: a query inside another query

A subquery is an inner query whose result is used by an outer query. It is helpful when a business comparison depends on a value that must first be calculated from the same dataset.

SQL
SELECT order_id, total_amount
FROM orders
WHERE total_amount > (
  SELECT AVG(total_amount)
  FROM orders
);
Token Explanation
Outer SELECT Returns order IDs and amounts.
FROM orders Reads the order rows to compare.
WHERE total_amount > Keeps orders above a calculated benchmark.
( ... ) Groups the inner query as one logical value.
SELECT AVG(total_amount) Calculates the average order amount.

⚡ Level Up Quiz: Advanced Analysis

1. A BA wants regions whose total sales exceed $100,000. Which clause filters the regions?
2. Which join keeps customers even if they have no orders?
3. What does SUM(total_amount) do?
4. You need every customer, including customers with no orders. Which join should you start with?
06

Importing & Exporting Data

Move normal relational MySQL data safely between systems and tools.

First understand the two kinds of export

A SQL dump is a backup or migration file containing SQL instructions such as CREATE DATABASE, CREATE TABLE, and INSERT. It can recreate structure and data in another MySQL server. A CSV export is a flat text file containing rows and separators. It is excellent for Excel, Power BI, or sharing a filtered report, but it does not remember primary keys, foreign keys, data types, indexes, or relationships.

FormatBest forWhat it preservesMain limitation
.sql dumpBackup, migration, moving a databaseTables, data, keys, indexes, and sometimes usersUsually needs MySQL or a compatible database to open
.csvExcel, Power BI, simple data sharingVisible rows and columns onlyNo relationships, types, or constraints
Workbench result exportExporting the result of a specific analysisThe rows returned by your SELECTUsually exports only the result, not the database design

Export a complete database to a .sql file

The standard tool is mysqldump. It reads the database and writes SQL statements to a file. This is the correct choice when you want a restorable backup or need to move your development database to another machine.

Terminal
# Export one database: structure and rows
mysqldump -u root -p northwind > northwind_backup.sql

# Export one table only
mysqldump -u root -p northwind orders > orders_backup.sql

# Export several named tables
mysqldump -u root -p northwind customers orders > customer_orders.sql

# Export every database on the server
mysqldump -u root -p --all-databases > all_databases.sql
PartMeaning
mysqldumpMySQL's command-line export utility.
-u rootConnect using the root account; use a permitted account in professional environments.
-pPrompt for the password instead of placing it visibly in shell history.
northwindThe database being exported.
ordersOptional table name; omit it when the complete database is required.
>Shell redirection: writes the command output into a file.
northwind_backup.sqlThe file that receives the SQL dump.
Backup habit: A backup is only trustworthy if you test restoring it. Keep the file in a protected location, do not commit real customer data to Git, and never paste database passwords into commands that become visible in history.

Import a .sql file from the command line

Importing means sending the SQL statements inside the file to a MySQL server. For a full dump, the file may create the database and tables itself. For a dump that does not create the database, create and select the destination first.

Terminal
# Import into an existing database
mysql -u root -p northwind < northwind_backup.sql

# Import a dump that contains CREATE DATABASE statements
mysql -u root -p < northwind_backup.sql

# Alternative: open the client, select a database, then run the file
mysql -u root -p
USE northwind;
SOURCE C:/backups/northwind_backup.sql;
PartMeaning
mysqlStarts the MySQL command-line client.
northwindRoutes statements to this selected database.
<Shell input redirection: sends the file into the client.
SOURCEMySQL client command that executes a local SQL file.
file pathThe exact location of the dump; Windows paths are safest with forward slashes in SOURCE.

Verify the import

SQL
SHOW DATABASES;
USE northwind;
SHOW TABLES;
SELECT COUNT(*) FROM customers;
SELECT COUNT(*) FROM orders;

Do not assume that “the import completed” means it is correct. Compare table counts, sample a few rows, and validate key relationships.

Export a query result to CSV

CSV export is perfect when a stakeholder needs the output of a specific SELECT rather than an entire database. The query below creates a file with one header row and one row per paid order.

SQL
SELECT
  'order_id', 'customer_id', 'order_date', 'total_amount'
UNION ALL
SELECT
  order_id, customer_id, order_date, total_amount
FROM orders
WHERE status = 'Paid'
INTO OUTFILE '/var/lib/mysql-files/paid_orders.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n';
PartWhy it is used
UNION ALLPlaces a manually written header row above the data rows.
INTO OUTFILETells MySQL to write the result into a server-side file.
FIELDS TERMINATED BY ','Uses commas between columns.
ENCLOSED BY '"'Wraps values in quotes, helping text containing commas remain safe.
LINES TERMINATED BY '\n'Places every result row on a new line.
Important: INTO OUTFILE writes on the database server, not necessarily on your laptop. MySQL may restrict the folder using secure_file_priv. In Workbench, the easier beginner workflow is: run your SELECT, click the result-grid export button, choose CSV, and select a local folder.

Import a CSV into a normal table

CSV has no schema, so create the destination table first. Then tell MySQL which separator, quote character, line ending, and column order the file uses.

SQL
CREATE TABLE imported_customers (
  customer_id INT,
  full_name VARCHAR(100),
  email VARCHAR(255),
  signup_date DATE
);

LOAD DATA LOCAL INFILE 'C:/imports/customers.csv'
INTO TABLE imported_customers
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 ROWS;
PartMeaning
CREATE TABLEDefines the expected columns and types before data enters MySQL.
LOAD DATA LOCAL INFILEReads a CSV from the client computer and sends it to MySQL.
INTO TABLENames the destination table.
FIELDS TERMINATED BYDefines the column separator, usually a comma.
LINES TERMINATED BYDefines the row ending; Windows CSV files may need '\r\n'.
IGNORE 1 ROWSSkips the first line when it contains column headings.

CSV import checklist

  1. Confirm the CSV column order matches the table or provide a column list.
  2. Check dates use a consistent format such as YYYY-MM-DD.
  3. Remove currency symbols and thousands separators from numeric fields.
  4. Load into a staging table first, then validate before moving to production tables.
  5. Count imported rows and inspect rejected or incorrectly converted values.

Workbench import and export workflow

  1. Export a full SQL dump: open Workbench, choose Server, Data Export, select the schema, choose Export to Self-Contained File, and start the export.
  2. Import a SQL dump: choose Server, Data Import, select Import from Self-Contained File, choose the target schema, and start the import.
  3. Export CSV: execute a SELECT, then use the result grid's export icon and choose CSV.
  4. Import CSV: right-click a table, choose Table Data Import Wizard, select the CSV, map columns, preview, and execute.
Which should you choose? Use a SQL dump for backup or migration. Use CSV for a filtered report or spreadsheet handoff. CSV is not a complete database backup.

⚡ Level Up Quiz: Import & Export

1. You need to move tables, relationships, and data to another MySQL server. Which format is best?
2. What does the < symbol do in mysql database < backup.sql?
3. Why is CSV not a complete database backup?
4. Why should a CSV usually be imported into a staging table first?
07

Business Analyst Use Cases

Move from syntax to real business decisions.

Use Case 1: Top five customers by revenue

Business question: “Which five customers generated the most paid revenue this year?” Before writing SQL, define revenue, paid status, date range, and grain. The grain should be one row per customer.
SQL
SELECT
  c.customer_id,
  c.full_name,
  SUM(o.total_amount) AS revenue
FROM customers AS c
INNER JOIN orders AS o
  ON o.customer_id = c.customer_id
WHERE o.status = 'Paid'
GROUP BY c.customer_id, c.full_name
ORDER BY revenue DESC
LIMIT 5;
Line Why it exists
SELECT Starts the report request.
c.customer_id Provides a stable customer identity.
c.full_name Provides a human-readable customer name.
SUM(o.total_amount) Adds all order amounts for each customer.
AS revenue Gives the calculated output a clear business name.
FROM customers c Starts with customer identity information.
INNER JOIN orders o Connects customers to their transactions.
ON o.customer_id = c.customer_id Defines how the tables match.
WHERE status = 'Paid' Excludes unpaid, cancelled, or pending orders.
GROUP BY Creates one result row per customer.
ORDER BY revenue DESC Ranks the highest revenue customers first.
LIMIT 5 Returns only the requested five customers.

Use Case 2: Monthly active users

Business question: “How many unique users logged in during each month?” One user may create many login events, so the query must count distinct users, not total login rows.
SQL
SELECT
  DATE_FORMAT(event_time, '%Y-%m-01') AS month_start,
  COUNT(DISTINCT user_id) AS active_users
FROM user_events
WHERE event_name = 'login'
GROUP BY month_start
ORDER BY month_start;
Part Explanation
DATE_FORMAT Transforms a timestamp into a reporting month.
'%Y-%m-01' Formats the value as the first day of its month.
COUNT(DISTINCT user_id) Counts each unique user once per month.
FROM user_events Reads event records.
WHERE event_name = 'login' Counts login activity only.
GROUP BY month_start Creates one result row per month.
ORDER BY month_start Displays the months chronologically.
Important BA assumptions: Confirm the timezone, whether internal employees are excluded, what counts as a login, and whether bot activity is removed.

Use Case 3: Identify missing data

Business question: “Which customers are missing contact information, and which customers have never placed an order?”
SQL
-- Missing email addresses
SELECT customer_id, full_name
FROM customers
WHERE email IS NULL;

-- Customers without an order
SELECT c.customer_id, c.full_name
FROM customers AS c
LEFT JOIN orders AS o
  ON o.customer_id = c.customer_id
WHERE o.order_id IS NULL;
Part Explanation
email IS NULL Correctly finds records where email is missing.
LEFT JOIN Keeps every customer, even if no order exists.
o.order_id IS NULL Identifies customers for whom no matching order was found.

A strong BA does not stop at “there are 500 missing records.” You should identify the source process, business owner, defect severity, correction plan, and trend over time.

Deep Practice Studio: translate a stakeholder request

Stakeholder request: “Show me customers who bought more than ₹50,000 in the last 90 days, rank them from highest to lowest, and include customers with no recent purchase so the retention team can follow up.”

Step 1 — clarify the request before writing SQL

  • Grain: one output row per customer.
  • Measure: SUM of order revenue, not a count of orders.
  • Time rule: order_date is greater than or equal to 90 days ago.
  • Population: all customers, including those with no qualifying order.
  • Business decision: high-value customers and retention follow-up.

Step 2 — build the query in layers

SQL · final version
SELECT
    c.customer_id,
    CONCAT(c.first_name, ' ', c.last_name) AS customer_name,
    COALESCE(SUM(o.quantity * p.unit_price), 0) AS recent_revenue
FROM customers c
LEFT JOIN orders o
    ON o.customer_id = c.customer_id
   AND o.order_date >= DATE_SUB(CURDATE(), INTERVAL 90 DAY)
LEFT JOIN products p
    ON p.product_id = o.product_id
GROUP BY c.customer_id, c.first_name, c.last_name
ORDER BY recent_revenue DESC;
LineWhy it existsCommon beginner mistake
SELECTChooses the final report columns.Selecting raw order rows instead of customer-level output.
CONCAT(...)Creates one readable display name from two columns.Assuming first_name and last_name are one stored column.
COALESCE(SUM(...), 0)Turns a customer with no matching order into revenue 0 rather than NULL.Thinking NULL means zero automatically.
LEFT JOIN ordersPreserves every customer for retention analysis.Using INNER JOIN and silently losing customers with no order.
AND inside ONApplies the 90-day rule while preserving unmatched customers.Putting the date condition in WHERE, which would remove NULL order rows.
GROUP BYReturns one row per customer after multiplying quantity by price.Grouping only by customer_id while selecting non-aggregated names in strict mode.
ORDER BYRanks the output from highest recent revenue to lowest.Assuming database rows arrive in a useful order.
Analyst validation: before presenting this output, compare the total revenue to a trusted finance report, check whether refunds and tax are included, verify the timezone behind CURDATE(), and test one customer's numbers manually.

The BA SQL workflow

1. Question Write the business question in plain language.
2. Grain Decide what one result row represents.
3. Source Identify tables, relationships, and trustworthy columns.
4. Validate Compare results with a known total and document assumptions.
Professional habit: SQL is not the final business deliverable. The final deliverable is a trustworthy answer that explains the definition, data source, filters, limitations, and decision supported by the result.

⚡ Level Up Quiz: Business Analyst Use Cases

1. Why use COUNT(DISTINCT user_id) for monthly active users?
2. Which approach finds customers who never placed an order?
3. What should accompany an important KPI query?
4. The top-customer report suddenly doubles revenue after a new JOIN. What should you investigate first?