Free tutorials & notes in Hindi & English · Clean code examples 🧰 मुफ़्त PDF और फ़ोटो टूल्स →
🛢️ Week 6 – Data Science के लिए SQL  ·  दिन 35

दिन 35: Project: SQL + Pandas HR Analytics

⏱ 3–4 hours📶 Project🎯 100% self-study
दिन 35 / 60

✅ आज आप सीखेंगे

  • छोटा star schema (fact + dimensions) design करना
  • Keys के साथ tables बनाकर data load करना
  • Reusable analysis VIEW बनाना
  • SQL में 8 business सवालों के जवाब
  • Charts और report के लिए results pandas को देना

🏢 असली दुनिया में

BazaarKart Excel से database पर जा रहा है। आपको mini data warehouse बनाना है, 2024 के orders load करने हैं, और quarterly business review तैयार करना है: किन शहरों ने target पूरे किए, कौन बढ़ रहा है, कौन से products आगे हैं, और customer segments कैसे व्यवहार करते हैं। Junior analysts अपने पहले महीनों में बिल्कुल यही करते हैं।

Step 1: schema design

fact_ordersorder_id PKorder_datecity FKproduct FKquantityunit_pricediscountpayment_moderatingdim_citycity PKregiondim_productproduct PKcategorycity_targetscityquartertarget_lakh
Star schema: बीच में fact table, चारों ओर dimensions

Region और category हर order row पर नहीं, dimension tables में एक बार रखे जाते हैं – कम दोहराव और typo ठीक करने की एक ही जगह।

Step 2: बनाना और load करना

Python
import sqlite3
import pandas as pd
s = pd.read_csv("sales_data.csv")
con = sqlite3.connect("bazaarkart_dw.db")
con.executescript("""
DROP TABLE IF EXISTS fact_orders; DROP TABLE IF EXISTS dim_product;
DROP TABLE IF EXISTS dim_city;    DROP TABLE IF EXISTS city_targets;
CREATE TABLE dim_city (city TEXT PRIMARY KEY, region TEXT NOT NULL);
CREATE TABLE dim_product (product TEXT PRIMARY KEY, category TEXT NOT NULL);
CREATE TABLE fact_orders (
    order_id TEXT PRIMARY KEY, order_date TEXT, city TEXT REFERENCES dim_city(city),
    product TEXT REFERENCES dim_product(product), quantity INTEGER, unit_price REAL,
    discount REAL, payment_mode TEXT, customer_age REAL, customer_gender TEXT, rating REAL);
CREATE TABLE city_targets (city TEXT, quarter INTEGER, target_lakh REAL);
""")
s[["city", "region"]].drop_duplicates().to_sql("dim_city", con, if_exists="append", index=False)
s[["product", "category"]].drop_duplicates().to_sql("dim_product", con, if_exists="append", index=False)
s.drop(columns=["region", "category"]).to_sql("fact_orders", con, if_exists="append", index=False)
targets = [(c, qtr, t) for c, t in [("Mumbai", 2.4), ("Bengaluru", 2.4), ("Delhi", 2.3), ("Kolkata", 2.0),
           ("Hyderabad", 1.5), ("Chennai", 1.4), ("Patna", 1.1), ("Ahmedabad", 1.1), ("Pune", 1.2),
           ("Lucknow", 1.0), ("Jaipur", 0.7)] for qtr in (1, 2, 3, 4)]
con.executemany("INSERT INTO city_targets VALUES (?, ?, ?)", targets)
con.execute("CREATE INDEX idx_orders_date ON fact_orders(order_date)")
con.commit()
for t in ["dim_city", "dim_product", "fact_orders", "city_targets"]:
    print(t, con.execute(f"SELECT COUNT(*) FROM {t}").fetchone()[0], "rows")
dim_city 11 rows dim_product 25 rows fact_orders 2000 rows city_targets 44 rows
Python
import sqlite3
import pandas as pd
con = sqlite3.connect("bazaarkart_dw.db")
q = lambda sql: pd.read_sql(sql, con)
con.executescript("""
DROP VIEW IF EXISTS v_orders;
CREATE VIEW v_orders AS
SELECT o.*, c.region, p.category,
       o.quantity * o.unit_price * (1 - o.discount) AS revenue,
       CAST(strftime('%m', o.order_date) AS INTEGER) AS month,
       (CAST(strftime('%m', o.order_date) AS INTEGER) + 2) / 3 AS quarter
FROM fact_orders o
JOIN dim_city c    ON o.city = c.city
JOIN dim_product p ON o.product = p.product;
""")
print(q("SELECT order_id, city, region, category, ROUND(revenue) AS revenue, quarter FROM v_orders LIMIT 4"))
order_id city region category revenue quarter 0 BK100359 Delhi North Fashion 2440.0 1 1 BK100422 Mumbai West Fashion 2094.0 1 2 BK100744 Mumbai West Grocery 2625.0 1 3 BK100830 Mumbai West Books & Stationery 139.0 1

Q1: क्या हर शहर ने quarterly target पूरा किया?

Python
import sqlite3
import pandas as pd
con = sqlite3.connect("bazaarkart_dw.db")
q = lambda sql: pd.read_sql(sql, con)
tv = q("""
WITH actual AS (
    SELECT city, quarter, SUM(revenue) / 1e5 AS actual_lakh FROM v_orders GROUP BY city, quarter
)
SELECT t.city, t.quarter, t.target_lakh, ROUND(a.actual_lakh, 2) AS actual_lakh,
       ROUND(100 * a.actual_lakh / t.target_lakh) AS achievement_pct
FROM city_targets t LEFT JOIN actual a ON a.city = t.city AND a.quarter = t.quarter
""")
pv = tv.pivot(index="city", columns="quarter", values="achievement_pct")
print(pv.astype(int))
print("City-quarters on target:", (tv["achievement_pct"] >= 100).sum(), "of", len(tv))
quarter 1 2 3 4 city Ahmedabad 122 57 90 140 Bengaluru 118 85 74 124 Chennai 65 124 82 119 Delhi 87 67 113 143 Hyderabad 102 73 42 164 Jaipur 120 57 63 131 Kolkata 61 113 86 160 Lucknow 61 61 116 157 Mumbai 163 72 80 120 Patna 198 45 47 129 Pune 67 73 74 123 City-quarters on target: 21 of 44
Python
import sqlite3
import pandas as pd
con = sqlite3.connect("bazaarkart_dw.db")
q = lambda sql: pd.read_sql(sql, con)
import seaborn as sns
import matplotlib.pyplot as plt
tv = q("""WITH a AS (SELECT city, quarter, SUM(revenue) / 1e5 AS act FROM v_orders GROUP BY city, quarter)
SELECT t.city, t.quarter, ROUND(100 * a.act / t.target_lakh) AS pct
FROM city_targets t JOIN a ON a.city = t.city AND a.quarter = t.quarter""")
pv = tv.pivot(index="city", columns="quarter", values="pct")
pv = pv.loc[pv.mean(axis=1).sort_values(ascending=False).index]
plt.figure(figsize=(7, 5))
sns.heatmap(pv, annot=True, fmt=".0f", cmap="RdYlGn", center=100, cbar_kws=dict(label="% of target"))
plt.title("Target achievement by city and quarter (%)"); plt.xlabel("Quarter"); plt.ylabel("")
plt.show()
शहर और quarter के हिसाब से target achievement heatmap
हरा ≥ 100%। Q4 (festive) लगभग हर जगह हरा; Q2–Q3 समस्या वाले quarters।

Q2: H1 से H2 में कौन सा region सबसे तेज़ बढ़ा?

Python
import sqlite3
import pandas as pd
con = sqlite3.connect("bazaarkart_dw.db")
q = lambda sql: pd.read_sql(sql, con)
print(q("""
SELECT region,
       ROUND(SUM(CASE WHEN month <= 6 THEN revenue END) / 1e5, 2) AS h1_lakh,
       ROUND(SUM(CASE WHEN month > 6  THEN revenue END) / 1e5, 2) AS h2_lakh,
       ROUND(100.0 * (SUM(CASE WHEN month > 6 THEN revenue END) - SUM(CASE WHEN month <= 6 THEN revenue END))
             / SUM(CASE WHEN month <= 6 THEN revenue END), 1) AS growth_pct
FROM v_orders GROUP BY region ORDER BY growth_pct DESC
"""))
region h1_lakh h2_lakh growth_pct 0 North 6.01 10.00 66.5 1 East 6.16 6.84 11.1 2 South 10.15 10.67 5.2 3 West 9.27 9.71 4.8
💡 Conditional aggregation

SUM(CASE WHEN … THEN revenue END) SQL के अंदर data pivot करता है – reports का सबसे काम का pattern।

Q3: हर category का top product और उसका हिस्सा

Python
import sqlite3
import pandas as pd
con = sqlite3.connect("bazaarkart_dw.db")
q = lambda sql: pd.read_sql(sql, con)
print(q("""
WITH p AS (
    SELECT category, product, SUM(revenue) AS rev,
           SUM(SUM(revenue)) OVER (PARTITION BY category) AS cat_rev,
           ROW_NUMBER() OVER (PARTITION BY category ORDER BY SUM(revenue) DESC) AS rn
    FROM v_orders GROUP BY category, product
)
SELECT category, product, ROUND(rev) AS revenue, ROUND(100 * rev / cat_rev, 1) AS share_of_category_pct
FROM p WHERE rn = 1 ORDER BY revenue DESC
"""))
category product revenue share_of_category_pct 0 Electronics Mobile Phone 1801233.0 46.9 1 Fashion Sneakers 604886.0 39.9 2 Grocery Dry Fruits 1kg 247044.0 31.3 3 Home & Kitchen Mixer Grinder 209764.0 49.3 4 Books & Stationery NCERT Book Set 140285.0 45.3

Q4: Prepaid vs COD – value और संतुष्टि

Python
import sqlite3
import pandas as pd
con = sqlite3.connect("bazaarkart_dw.db")
q = lambda sql: pd.read_sql(sql, con)
print(q("""
SELECT CASE WHEN payment_mode = 'Cash on Delivery' THEN 'COD' ELSE 'Prepaid' END AS pay_type,
       COUNT(*) AS orders, ROUND(AVG(revenue)) AS aov, ROUND(AVG(rating), 2) AS avg_rating,
       ROUND(100.0 * SUM(CASE WHEN rating <= 3 THEN 1 ELSE 0 END) / COUNT(rating), 1) AS low_rating_pct
FROM v_orders GROUP BY pay_type
"""))
pay_type orders aov avg_rating low_rating_pct 0 COD 391 4142.0 4.06 11.2 1 Prepaid 1609 3269.0 4.06 12.7

Q5: Customer age segments

Python
import sqlite3
import pandas as pd
con = sqlite3.connect("bazaarkart_dw.db")
q = lambda sql: pd.read_sql(sql, con)
print(q("""
SELECT CASE WHEN customer_age IS NULL THEN 'Unknown'
            WHEN customer_age < 25 THEN '18-24' WHEN customer_age < 35 THEN '25-34'
            WHEN customer_age < 45 THEN '35-44' ELSE '45+' END AS age_group,
       COUNT(*) AS orders, ROUND(SUM(revenue) / 1e5, 2) AS revenue_lakh,
       ROUND(100.0 * SUM(CASE WHEN category = 'Electronics' THEN revenue ELSE 0 END) / SUM(revenue), 1) AS electronics_share_pct
FROM v_orders GROUP BY age_group ORDER BY age_group
"""))
age_group orders revenue_lakh electronics_share_pct 0 18-24 429 14.41 57.3 1 25-34 816 29.76 57.3 2 35-44 570 17.26 49.1 3 45+ 160 6.86 64.6 4 Unknown 25 0.51 35.0

Q6: सबसे अच्छे और बुरे हफ़्ते

Python
import sqlite3
import pandas as pd
con = sqlite3.connect("bazaarkart_dw.db")
q = lambda sql: pd.read_sql(sql, con)
w = q("""
SELECT strftime('%W', order_date) AS week, MIN(order_date) AS week_start,
       COUNT(*) AS orders, ROUND(SUM(revenue) / 1e5, 2) AS revenue_lakh
FROM v_orders GROUP BY week
""")
print("Best 3 weeks:\n", w.nlargest(3, "revenue_lakh").to_string(index=False))
print("Worst 3 weeks:\n", w.nsmallest(3, "revenue_lakh").to_string(index=False))
Best 3 weeks: week week_start orders revenue_lakh 45 2024-11-04 70 4.13 47 2024-11-18 74 2.13 40 2024-09-30 54 2.12 Worst 3 weeks: week week_start orders revenue_lakh 53 2024-12-30 12 0.31 17 2024-04-22 22 0.51 27 2024-07-01 17 0.54

Q7: Q4 में quarter-on-quarter growth के हिसाब से शहर

Python
import sqlite3
import pandas as pd
con = sqlite3.connect("bazaarkart_dw.db")
q = lambda sql: pd.read_sql(sql, con)
print(q("""
WITH cq AS (SELECT city, quarter, SUM(revenue) AS rev FROM v_orders GROUP BY city, quarter),
g AS (SELECT city, quarter, rev, LAG(rev) OVER (PARTITION BY city ORDER BY quarter) AS prev FROM cq)
SELECT city, ROUND(prev / 1e5, 2) AS q3_lakh, ROUND(rev / 1e5, 2) AS q4_lakh,
       ROUND(100 * (rev - prev) / prev, 1) AS qoq_pct,
       RANK() OVER (ORDER BY (rev - prev) / prev DESC) AS rnk
FROM g WHERE quarter = 4 ORDER BY rnk
"""))
city q3_lakh q4_lakh qoq_pct rnk 0 Hyderabad 0.63 2.46 287.4 1 1 Patna 0.51 1.42 175.4 2 2 Jaipur 0.44 0.92 106.0 3 3 Kolkata 1.72 3.19 86.0 4 4 Bengaluru 1.78 2.98 67.2 5 5 Pune 0.89 1.48 66.9 6 6 Ahmedabad 0.99 1.54 55.2 7 7 Mumbai 1.92 2.89 50.4 8 8 Chennai 1.15 1.66 45.0 9 9 Lucknow 1.16 1.57 35.6 10 10 Delhi 2.61 3.30 26.5 11

Q8: Management के लिए summary table export

Python
import sqlite3
import pandas as pd
con = sqlite3.connect("bazaarkart_dw.db")
q = lambda sql: pd.read_sql(sql, con)
summary = q("""
SELECT region, city,
       COUNT(*) AS orders, ROUND(SUM(revenue)) AS revenue, ROUND(AVG(revenue)) AS aov,
       ROUND(AVG(rating), 2) AS rating,
       ROUND(100.0 * SUM(CASE WHEN payment_mode <> 'Cash on Delivery' THEN 1 ELSE 0 END) / COUNT(*), 1) AS prepaid_pct
FROM v_orders GROUP BY region, city ORDER BY revenue DESC
""")
summary.to_csv("qbr_city_summary.csv", index=False)
print(summary.head(6).to_string(index=False))
print("Saved qbr_city_summary.csv with", len(summary), "rows")
region city orders revenue aov rating prepaid_pct West Mumbai 252 1043230.0 4140.0 3.99 77.8 South Bengaluru 299 964020.0 3224.0 4.07 80.6 North Delhi 271 945592.0 3489.0 4.00 79.3 East Kolkata 193 839885.0 4352.0 4.06 82.4 South Hyderabad 200 572190.0 2861.0 4.09 77.5 South Chennai 155 545479.0 3519.0 4.16 84.5 Saved qbr_city_summary.csv with 11 rows

Quarterly business review के निष्कर्ष

🎯
Targets मौसमी हैंबराबर quarterly targets Q2–Q3 में चूकते और Q4 में पार होते हैं। मौसमी targets तय करें।
📱
Phones और laptops आगेElectronics में एक-दो products ज़्यादातर revenue लाते हैं – पहले इनका stock।
💳
Prepaid बढ़ाएँUPI cashback का आकार तय करने के लिए COD vs prepaid AOV और ratings की तुलना।
🗄️
एक ही सच का स्रोतv_orders view हर analyst को revenue की एक ही परिभाषा देता है।
Database क्या बताता है

🏅 आज के Job Skills

✔ Star schema design करके बनाना✔ Data load करना और views/indexes बनाना✔ CTEs और windows से business सवालों के जवाब✔ SQL और pandas को report में जोड़ना

Practice tasks

🏋️ Practice – ख़ुद करें

Task 1: किस category में low ratings (≤ 3) का हिस्सा सबसे ज़्यादा था?
Solution देखें
Python
import sqlite3
import pandas as pd
con = sqlite3.connect("bazaarkart_dw.db")
q = lambda sql: pd.read_sql(sql, con)
print(q("""SELECT category, ROUND(100.0 * SUM(CASE WHEN rating <= 3 THEN 1 ELSE 0 END) / COUNT(rating), 1) AS low_pct
FROM v_orders GROUP BY category ORDER BY low_pct DESC"""))
category low_pct 0 Fashion 13.4 1 Electronics 13.2 2 Grocery 11.9 3 Home & Kitchen 11.5 4 Books & Stationery 10.7
Task 2: एक dim_date table (date, month, quarter, weekday, is_festive) जोड़ें और join करके बताएँ: festive vs normal औसत daily revenue।
Task 3: इस QBR के लिए 4 lines की management summary लिखें।

Quick quiz

🧠 Quick Quiz – ख़ुद को जाँचें

1. Star schema में fact table में क्या होता है?
Dimensions बताते हैं; facts मापते हैं।
2. VIEW क्या है?
यह rows नहीं, definition रखता है।
3. SUM(CASE WHEN month <= 6 THEN revenue END) क्या निकालता है?
Conditional aggregation।
4. order_date पर index मुख्य रूप से…
किताब के index जैसा।
5. Region को हर order की जगह dim_city में क्यों रखें?
Normalisation।

Interview corner

💼 Interview Corner

OLTP और OLAP databases में क्या फ़र्क है?

OLTP systems (app databases) बहुत सारे छोटे reads/writes संभालते हैं और normalised होते हैं। OLAP systems (BigQuery, Redshift, Snowflake जैसे warehouses) बड़ी analytical queries के लिए optimised होते हैं, अक्सर star schemas के साथ।

अपना कोई SQL project बताइए।

यह project: star schema design किया, 2,000 orders load किए, एक revenue definition वाला reusable view बनाया, और CTEs व window functions से target achievement, growth, product ranking और segment सवालों के जवाब दिए; QBR deck के लिए results export किए।

सारांश

  • Star schema: keys से जुड़ी fact table + dimension tables।
  • Views सबको metrics की एक परिभाषा देते हैं।
  • Conditional aggregation, CTEs और window functions असली business सवालों के जवाब देते हैं।
  • Week 6 पूरा – SQL ✔। अगला पड़ाव: machine learning!
➡️ आगे

Week 7: Machine Learning। Day 36: ML असल में क्या है, learning के प्रकार और ML workflow।

अक्सर पूछे जाने वाले प्रश्न (FAQ)

क्या SQLite की जगह MySQL में कर सकते हैं?
हाँ। strftime की जगह DATE_FORMAT/MONTH()/QUARTER() लें और database MySQL Workbench या phpMyAdmin में बनाएँ; बाकी सब वही।
Analysis SQL में करें या pandas में?
भारी filtering, joining और aggregation SQL में (data के पास), फिर छोटे result पर statistics, charts और modelling pandas में।
← Data Science (60 Days)
🔗

Share this topic with a friend

यह topic किसी दोस्त को भेजें

Found it useful? Send it to a classmate learning the same thing.

अच्छा लगा? जो दोस्त यही सीख रहा है, उसे भेज दीजिए।

अगला topicदिन 36: Machine Learning क्या है? आपकी प्रगति: 35 / 60 (58%) · Lesson 36 / 60 आगे बढ़ें →

यह lesson कैसा लगा?

आपकी राय से हम content और बेहतर बनाते हैं।

अपना review और सुझाव लिखें (वैकल्पिक)

Review approve होने के बाद आपका नाम और शहर इसके साथ public दिख सकते हैं। Email या phone न लिखें।

▶ CodingEasily on YouTube

Video से सीखें – आसान Hindi में

चुनी हुई video: RDBMS भाग 3 – Class XI Commerce IT

▶ Channel subscribe करें