Day 32: SQL Basics with SQLite
✅ Today you will learn
- Why every data job asks for SQL
- Create a database from CSV files
- SELECT, aliases and calculated columns
- Filter with WHERE, IN, BETWEEN, LIKE, IS NULL
- Sort, limit, DISTINCT and CASE WHEN
🏢 In the real world
In a real company the data does not live in CSV files on your laptop – it lives in databases with crores of rows. Before you can use pandas you must pull the right slice with SQL. It is the #1 skill in data-analyst job descriptions in India.
SQL in one picture
We use SQLite – a full SQL database inside one file that comes with Python. The SQL you write here works almost unchanged in MySQL and PostgreSQL.
import sqlite3
import pandas as pd
con = sqlite3.connect("bazaar.db")
pd.read_csv("sales_data.csv").to_sql("sales", con, if_exists="replace", index=False)
pd.read_csv("employees.csv").to_sql("employees", con, if_exists="replace", index=False)
pd.read_csv("departments.csv").to_sql("departments", con, if_exists="replace", index=False)
def q(sql):
return pd.read_sql(sql, con)
print(q("SELECT name, type FROM sqlite_master WHERE type = 'table'"))SELECT
import sqlite3
import pandas as pd
con = sqlite3.connect("bazaar.db")
q = lambda sql: pd.read_sql(sql, con)
print(q("SELECT order_id, city, product, unit_price FROM sales LIMIT 5"))import sqlite3
import pandas as pd
con = sqlite3.connect("bazaar.db")
q = lambda sql: pd.read_sql(sql, con)
print(q("""
SELECT order_id,
product,
quantity * unit_price AS gross,
ROUND(quantity * unit_price * (1 - discount), 2) AS revenue
FROM sales
LIMIT 5
"""))Write keywords in CAPITALS, one clause per line, and give calculated columns a name with AS. SQL ignores case and line breaks, but your colleagues will thank you.
WHERE – filtering rows
import sqlite3
import pandas as pd
con = sqlite3.connect("bazaar.db")
q = lambda sql: pd.read_sql(sql, con)
print(q("""
SELECT order_id, city, product, unit_price, payment_mode
FROM sales
WHERE city = 'Mumbai' AND unit_price > 20000
"""))| Operator | Example | pandas equivalent |
|---|---|---|
| = , <>, <, >= | city = 'Delhi' | df.city == "Delhi" |
| AND / OR / NOT | a > 1 AND b = 'x' | (a > 1) & (b == "x") |
| IN | city IN ('Pune', 'Delhi') | df.city.isin([...]) |
| BETWEEN | customer_age BETWEEN 25 AND 35 | between(25, 35) |
| LIKE | product LIKE '%Phone%' | str.contains("Phone") |
| IS NULL | rating IS NULL | df.rating.isna() |
import sqlite3
import pandas as pd
con = sqlite3.connect("bazaar.db")
q = lambda sql: pd.read_sql(sql, con)
print(q("""
SELECT COUNT(*) AS orders
FROM sales
WHERE city IN ('Delhi', 'Mumbai', 'Bengaluru')
AND customer_age BETWEEN 25 AND 35
AND product LIKE '%Phone%'
"""))
print(q("SELECT COUNT(*) AS missing_ratings FROM sales WHERE rating IS NULL"))WHERE rating = NULL returns nothing, even when ratings are missing. Always write IS NULL / IS NOT NULL.
ORDER BY, LIMIT, DISTINCT
import sqlite3
import pandas as pd
con = sqlite3.connect("bazaar.db")
q = lambda sql: pd.read_sql(sql, con)
print(q("""
SELECT order_id, city, product,
ROUND(quantity * unit_price * (1 - discount)) AS revenue
FROM sales
ORDER BY revenue DESC
LIMIT 5
"""))
print(q("SELECT DISTINCT region, city FROM sales ORDER BY region, city"))CASE WHEN – if/else inside SQL
import sqlite3
import pandas as pd
con = sqlite3.connect("bazaar.db")
q = lambda sql: pd.read_sql(sql, con)
print(q("""
SELECT order_id, unit_price,
CASE WHEN unit_price >= 20000 THEN 'Premium'
WHEN unit_price >= 2000 THEN 'Mid'
ELSE 'Budget' END AS price_band,
CASE WHEN payment_mode = 'Cash on Delivery' THEN 0 ELSE 1 END AS prepaid
FROM sales
ORDER BY unit_price DESC
LIMIT 6
"""))Working with dates
import sqlite3
import pandas as pd
con = sqlite3.connect("bazaar.db")
q = lambda sql: pd.read_sql(sql, con)
print(q("""
SELECT DISTINCT order_date,
strftime('%m', order_date) AS month,
CASE strftime('%w', order_date) WHEN '0' THEN 'Weekend' WHEN '6' THEN 'Weekend' ELSE 'Weekday' END AS day_type
FROM sales
WHERE order_date BETWEEN '2024-10-01' AND '2024-10-31'
ORDER BY order_date
LIMIT 7
"""))How SQL runs a query
- 1FROM (which table)
- 2WHERE (filter rows)
- 3GROUP BY (Day 33)
- 4HAVING (Day 33)
- 5SELECT (columns)
- 6ORDER BY
- 7LIMIT
Because WHERE runs before SELECT, you cannot use a SELECT alias in WHERE in most databases (WHERE revenue > 5000 fails). Repeat the expression or use a sub-query/CTE (Day 34). ORDER BY runs after SELECT, so aliases work there.
🏅 Job skills unlocked today
Practice tasks
🏋️ Practice – do it yourself
Show solution
import sqlite3
import pandas as pd
con = sqlite3.connect("bazaar.db")
q = lambda sql: pd.read_sql(sql, con)
print(q("""SELECT order_id, city, product, unit_price FROM sales
WHERE category = 'Electronics' AND region = 'South' ORDER BY unit_price LIMIT 5"""))Show solution
import sqlite3
import pandas as pd
con = sqlite3.connect("bazaar.db")
q = lambda sql: pd.read_sql(sql, con)
print(q("SELECT COUNT(*) AS n FROM employees WHERE salary BETWEEN 60000 AND 100000"))Show solution
import sqlite3
import pandas as pd
con = sqlite3.connect("bazaar.db")
q = lambda sql: pd.read_sql(sql, con)
print(q("""SELECT name, salary, CASE WHEN salary >= 100000 THEN 'Senior' WHEN salary >= 60000 THEN 'Mid' ELSE 'Junior' END AS band
FROM employees ORDER BY salary DESC LIMIT 8"""))Quick quiz
🧠 Quick Quiz – test yourself
Interview corner
💼 Interview Corner
What is the difference between WHERE and HAVING?
WHERE filters individual rows before grouping; HAVING filters groups after GROUP BY using aggregates (e.g. HAVING SUM(revenue) > 100000). Covered in Day 33.
Why can’t you use a column alias in WHERE?
Because of logical execution order – WHERE is evaluated before SELECT creates the alias. Repeat the expression or wrap the query in a sub-query/CTE.
Summary
- SELECT columns FROM table WHERE condition ORDER BY col LIMIT n.
- IN, BETWEEN, LIKE, IS NULL cover most filters.
- CASE WHEN creates categories; AS names columns.
- pd.read_sql brings results into pandas.
Day 33: GROUP BY, HAVING and JOINs – combine and summarise tables like a pro.
