100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
SQL Mastery
55 minbeginner

Setting Up PostgreSQL & First Queries

What You'll Build

In this hands-on exercise you will install PostgreSQL, create your own IPL cricket database, and load it with real-looking match data. You will then run a graded sequence of queries that exercise everything from Module 1: selecting columns, filtering with WHERE, and ranking results with ORDER BY and LIMIT, all against data you populated yourself.

By the end you will have a working local database and a query file that answers concrete cricket questions, such as the top run scorers and which batsmen stayed not out. This is the foundation every later module builds on, so getting a clean, reproducible setup now pays off through the entire course rather than fighting environment issues later.

The goal is fluency, not just correctness. You will type each query yourself, predict its output before running it, and compare against the expected result. That deliberate loop, predict then verify, is how query intuition is built, and it is far more valuable than copying answers, because it trains you to reason about what the engine will actually return.

Analogy🏏Cricket
🏏 Think of it like cricket: Think of the analytics team assembling the broadcast's end-of-day statistics package. Just as they compile separate panels, top scorers, team totals, venue records, and trend graphs, from the same ball-by-ball data, you build separate grouped queries from one deliveries table. Just as each panel must be accurate before it airs to millions, each query must be verified before it feeds the dashboard. Just as the package tells the day's story through numbers, your query layer turns raw deliveries into the season's narrative. Just as a wrong figure on air is caught by cross-checking panels against each other, team totals summing to the tournament total, your queries can be validated against one another for consistency. Just as the package is rebuilt fresh from the feed each day, every dashboard number is derived from raw rows at query time, never hand-edited. This reveals why disciplined aggregation is the backbone of every stats broadcast.

Prerequisites

  • A computer running Windows, macOS, or Linux with permission to install software and roughly 300 MB of free disk space available.
  • Basic comfort with a terminal or command prompt, since you will run a few commands to start the database and open its shell.
  • Completion of Module 1 reading lessons on SELECT, WHERE, and ORDER BY, whose concepts every exercise query below directly applies.
  • A plain text editor to save your queries in a .sql file so your work is reproducible and easy to rerun from scratch.

Setup & Project Structure

You will install the PostgreSQL server and its command-line client, psql. The server is the engine that stores data and runs queries; psql is the interactive shell you type SQL into. We will keep all work in a single project folder containing one schema file that creates and seeds tables, and one queries file holding the exercise answers.

Analogy🏏Cricket
🏏 Think of it like cricket: Building the deliveries table with season, match_date, and dismissal columns is like laying out a season-long scorebook designed for every question you will ask. Just as a good scorebook records not only runs but the date and manner of each dismissal so any summary can be drawn later, your enriched table carries the extra columns every aggregation and date query will need. Just as a scorer keeps the blank scorebook template so next season starts identically, your schema lives in a rerunnable script. Just as all the tournament's summary graphics are produced from that one book, every dashboard panel lives in one queries file you can run end to end. Just as reopening the book regenerates any figure on demand, rerunning the file regenerates every number at once. This reveals why a well-designed table and scripted panels make a dashboard reproducible.

Keeping setup in a script rather than typing it once interactively means you can drop and rebuild the database at any time with a single command. This reproducibility is a core data-engineering habit: anyone, including future you, can recreate the exact same database state from the files, making debugging and collaboration vastly easier than relying on a fragile, hand-built database.

bash
# 1. Install PostgreSQL (choose your platform).

# macOS (Homebrew):
brew install postgresql@16
brew services start postgresql@16

# Ubuntu / Debian Linux:
sudo apt update
sudo apt install -y postgresql postgresql-client
sudo service postgresql start

# Windows: download the installer from postgresql.org and run it,
# then use the bundled "SQL Shell (psql)" application.

# 2. Create a project folder and files.
mkdir ipl_sql_lab
cd ipl_sql_lab
touch schema.sql        # creates and seeds tables
touch exercises.sql     # your query answers

# 3. Create a fresh database for this lab.
createdb ipl_lab

# 4. Open the interactive shell connected to that database.
psql ipl_lab
# You should see a prompt like:  ipl_lab=#
# Type \q to quit, \dt to list tables, \d players to describe a table.

Step 1 — Foundation

Step one builds the schema: a single deliveries table rich enough to practise every Module 1 concept. It includes numeric columns for runs, text columns for names and venues, and a nullable dismissal column so you can practise IS NULL filtering. Defining clear types and a primary key now gives the engine the structure it needs to validate every row you insert.

You will save this in schema.sql and run it once. The CREATE TABLE statement declares the structure, and the INSERT statements seed enough rows to make query results meaningful. Running the whole file at once demonstrates the reproducible workflow: the database state comes entirely from a script you can rerun anytime, not from manual one-off typing.

Analogy🏏Cricket
🏏 Think of it like cricket: Picture the groundstaff and scorers preparing before the season opener. Just as they set up the pitch, boundary, and a fresh scorebook with columns for every stat to be recorded, the schema lays out typed columns for runs, dates, and dismissals. Just as a complete scorebook lets every later statistic be computed accurately, a complete table lets every dashboard panel aggregate correctly. Just as the same setup repeats reliably each match, rerunning the schema rebuilds identical data. Just as the scorers agree before the first ball how a wide or a no-ball will be recorded, the schema fixes types and NULL conventions up front so every later aggregate interprets the data identically. Just as an incomplete scorebook column ruins every statistic that depends on it, a mistyped or missing column undermines every panel built on the table. This shows why a thorough foundation makes every later summary dependable.
sql
-- schema.sql : run with  psql ipl_lab -f schema.sql

-- Start clean so the script is fully reproducible.
DROP TABLE IF EXISTS deliveries;

CREATE TABLE deliveries (
    delivery_id  INTEGER PRIMARY KEY,
    batsman      TEXT    NOT NULL,
    bowler       TEXT,
    team_name    TEXT    NOT NULL,
    venue        TEXT    NOT NULL,
    runs_scored  INTEGER NOT NULL CHECK (runs_scored >= 0),
    strike_rate  NUMERIC,
    dismissal    TEXT          -- NULL means the batsman was not out
);

INSERT INTO deliveries
    (delivery_id, batsman, bowler, team_name, venue, runs_scored, strike_rate, dismissal) VALUES
    (1,  'Rohit Sharma',     'Bumrah',  'Mumbai Indians',      'Wankhede',    72, 142.0, NULL),
    (2,  'Virat Kohli',      'Chahar',  'Royal Challengers',   'Chinnaswamy', 45, 128.5, 'caught'),
    (3,  'MS Dhoni',         'Boult',   'Chennai Super Kings', 'Chepauk',     18, 150.0, 'bowled'),
    (4,  'Suryakumar Yadav', 'Rashid',  'Mumbai Indians',      'Wankhede',    61, 168.5, NULL),
    (5,  'Rishabh Pant',     'Bumrah',  'Delhi Capitals',      'Kotla',       89, 151.0, 'run out'),
    (6,  'Shubman Gill',     'Siraj',   'Gujarat Titans',      'Motera',      57, 129.4, 'lbw'),
    (7,  'Virat Kohli',      'Bumrah',  'Royal Challengers',   'Wankhede',    33, 110.0, 'caught'),
    (8,  'Rohit Sharma',     'Chahal',  'Mumbai Indians',      'Chinnaswamy', 12,  85.0, 'bowled'),
    (9,  'KL Rahul',         'Boult',   'Lucknow Super Giants','Lucknow',     74, 133.9, NULL),
    (10, 'Hardik Pandya',    'Rashid',  'Mumbai Indians',      'Motera',      46, 164.2, 'caught');

-- Quick sanity check after loading.
SELECT COUNT(*) AS total_rows FROM deliveries;

Step 2 — Core Logic

Now you write the core exercise queries that apply SELECT, WHERE, and ORDER BY. Each is a concrete cricket question with a single correct result against the seeded data. Save these in exercises.sql and run them one at a time, predicting the output first. The comments state each task so your file doubles as a self-contained, gradeable worksheet.

These queries deliberately revisit every Module 1 idea: explicit column selection, comparison and pattern filters, NULL handling, and ranked top-N results. Working through them in sequence cements how the clauses combine in a single statement, which is exactly the muscle memory you need before moving on to aggregations and joins in the modules that follow.

Analogy🏏Cricket
🏏 Think of it like cricket: Think of the broadcast analyst slicing the same day's play three ways for three on-screen panels. Just as they break the data down by team for one graphic, by player for another, and by venue for a third, each query groups the same table on a different column. Just as every panel must rank its entries clearly, you order each by its key aggregate. Just as the panels share one underlying scorecard, your queries share one deliveries table. Just as a panel showing only three teams instantly warns the analyst the feed is incomplete, eyeballing each query's row counts against known facts catches bugs early. Just as the director insists each graphic answers exactly one question cleanly, each query should group on exactly the dimension its panel needs. This reveals why a single clean dataset can power many distinct summary views.
sql
-- exercises.sql : run with  psql ipl_lab -f exercises.sql
-- Predict each result BEFORE running, then verify.

-- Q1. List every batsman and their runs, highest first.
SELECT batsman, runs_scored
FROM deliveries
ORDER BY runs_scored DESC, delivery_id ASC;

-- Q2. Show only innings of more than 50 runs.
SELECT batsman, runs_scored
FROM deliveries
WHERE runs_scored > 50
ORDER BY runs_scored DESC;

-- Q3. Find all not-out innings (dismissal is missing).
SELECT batsman, runs_scored, venue
FROM deliveries
WHERE dismissal IS NULL;

-- Q4. Innings by Mumbai Indians OR Delhi Capitals scoring at least 45.
SELECT batsman, team_name, runs_scored
FROM deliveries
WHERE (team_name = 'Mumbai Indians' OR team_name = 'Delhi Capitals')
  AND runs_scored >= 45
ORDER BY runs_scored DESC, delivery_id ASC;

-- Q5. Any innings played at a venue whose name starts with 'W'.
SELECT batsman, venue, runs_scored
FROM deliveries
WHERE venue LIKE 'W%';

-- Q6. The single top-scoring innings (top-N with a tie-breaker).
SELECT batsman, runs_scored, venue
FROM deliveries
ORDER BY runs_scored DESC, delivery_id ASC
LIMIT 1;

Step 3 — Integration & Enhancement

Step three combines clauses and adds computed columns to answer richer questions. You will alias derived values, build a clean leaderboard with a stable tie-breaker, and use DISTINCT to list unique venues. These queries integrate everything from Module 1 into single statements, mirroring how real reporting queries layer filtering, computation, ordering, and limiting all at once.

Pay attention to the integer-division guard and the deterministic ORDER BY here, because these are the exact details that separate a query that looks right from one that is right. Saving these enhanced queries alongside the earlier ones gives you a complete, reproducible worksheet that demonstrates genuine command of the module's material.

Analogy🏏Cricket
🏏 Think of it like cricket: Picture the analyst adding the advanced graphics that follow the basic scoreboard. Just as they show only players who met a minimum-innings cut for an awards panel and chart scoring momentum month by month, you apply HAVING thresholds and DATE_TRUNC trends. Just as these refined panels demand more care than a raw total, they require correct filter placement and NULL handling. Just as the full package tells a richer story, your integrated queries deliver a complete dashboard. Just as the momentum chart would mislead viewers if rain-abandoned fixtures were charted as scoring dips, your trend query must decide explicitly how missing and NULL data appear. Just as the awards cut-off is printed on the graphic so viewers trust the ranking, the HAVING threshold belongs visibly in the query, not buried in app code. This shows why combining the module's tools yields professional-grade reporting.
sql
-- exercises.sql (continued)

-- Q7. Leaderboard: name, runs, and runs-per-over-style rate,
--     forcing decimal division to avoid integer truncation.
SELECT batsman,
       runs_scored,
       ROUND(runs_scored * 1.0 / 6, 2) AS runs_per_six_balls
FROM deliveries
ORDER BY runs_scored DESC, delivery_id ASC;

-- Q8. Unique list of venues used in the dataset.
SELECT DISTINCT venue
FROM deliveries
ORDER BY venue ASC;

-- Q9. Top 3 strike rates among innings of at least 40 runs,
--     relabelled for a clean report.
SELECT batsman          AS player,
       runs_scored      AS runs,
       strike_rate      AS sr
FROM deliveries
WHERE runs_scored >= 40
ORDER BY strike_rate DESC, delivery_id ASC
LIMIT 3;

-- Q10. Page 2 of the full leaderboard, 3 rows per page.
SELECT batsman, runs_scored
FROM deliveries
ORDER BY runs_scored DESC, delivery_id ASC
LIMIT 3 OFFSET 3;

Step 4 — Testing & Verification

Finally you verify your work by running both files end to end and checking the outputs against the expected results below. Because everything lives in scripts, you can drop the database, recreate it, and rerun in seconds, confirming your setup is genuinely reproducible rather than dependent on lucky manual steps you cannot repeat.

Analogy🏏Cricket
🏏 Think of it like cricket: Rebuilding the whole dashboard and checking each panel is like reprinting the entire season summary from the scorebook and confirming every graphic matches the record. Just as a broadcaster would not trust a figure that only appears if statements are run in a lucky order, you rebuild from scratch so no result depends on the sequence you happened to type. Just as the scorer reruns the totals and cross-checks the Orange Cap, run rates, and dismissal counts against the book, you check each panel against the expected results below. Just as the season book lets them regenerate every graphic in minutes, your scripted logic regenerates the entire dashboard data layer in seconds. Just as matching every panel to the record proves the summary is trustworthy, matching outputs confirms your figures are reproducible and correct. This reveals why a clean full rebuild is the real proof of a dashboard's integrity.
bash
# Rebuild and run everything from scratch to prove reproducibility.
dropdb ipl_lab && createdb ipl_lab
psql ipl_lab -f schema.sql
psql ipl_lab -f exercises.sql

# EXPECTED KEY RESULTS (verify a few):
# schema.sql  -> total_rows = 10
# Q2 (>50 runs) returns 5 rows: Pant 89, Rohit 72, KL Rahul 74, Surya 61, Gill 57
#                (ordered: Pant 89, KL Rahul 74, Rohit 72, Surya 61, Gill 57)
# Q3 (not out) returns 3 rows: Rohit (72), Surya (61), KL Rahul (74)
# Q6 (top innings) -> Rishabh Pant, 89, Kotla
# Q8 (distinct venues) -> Chepauk, Chinnaswamy, Kotla, Lucknow, Motera, Wankhede
# Q10 (page 2) -> rows ranked 4th-6th: KL Rahul 74? recompute by running it.

# Tip: pipe a single query's output to verify row counts:
psql ipl_lab -c "SELECT COUNT(*) FROM deliveries WHERE runs_scored > 50;"
# Expected: 5

Warning: A frequent setup error is "psql: could not connect to server". This almost always means the PostgreSQL service is not running. Start it with brew services start postgresql@16 on macOS or sudo service postgresql start on Linux, then retry. Also confirm you created the database with createdb ipl_lab before trying to connect to it.

Extension Challenge: Extend the deliveries table with a match_date column of type DATE, reseed a few rows with dates across two months, then write a query returning only innings from the most recent month, ordered by date. This previews the date-filtering and ordering skills you will use heavily in Module 2's date functions.

  • Installing the PostgreSQL server plus the psql client gives you a local engine to run every query, the foundation for the whole course.
  • Keeping schema and queries in .sql files makes your database fully reproducible, so you can drop and rebuild identical state in seconds.
  • Defining clear column types, NOT NULL, and a CHECK constraint up front lets the engine reject invalid data the moment you insert it.
  • Predicting each query's result before running it, then verifying, builds genuine query intuition far faster than copying answers.
  • A single statement can combine SELECT, WHERE, ORDER BY, LIMIT, and computed aliases, mirroring how real reporting queries are built.
  • Forcing decimal division and adding a unique tie-breaker in ORDER BY are the small details that make queries correct, not just plausible.
Lesson 5 of 35
0% complete