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

दिन 34: Window Functions और CTEs

⏱ 2.5 hours📶 Advanced🎯 100% self-study
दिन 34 / 60

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

  • CTEs: पढ़ने में आसान multi-step queries
  • Window functions vs GROUP BY
  • ROW_NUMBER, RANK, DENSE_RANK और हर group के top-N
  • Running totals और moving averages
  • LAG/LEAD growth और NTILE segments

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

“हर शहर के top 2 products”, “month-over-month growth”, “सालाना target की ओर cumulative revenue”, “हर department के अंदर salary rank” – ये चार सवाल लगभग हर data-analyst SQL round में आते हैं, और चारों के लिए window functions चाहिए।

Python – setup
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)
print([r[0] for r in con.execute("SELECT name FROM sqlite_master WHERE type = 'table'")])
['sales', 'employees', 'departments']

CTEs – WITH

CTE एक sub-query को नाम देता है ताकि query steps में बना सकें – Python के variables जैसा।

Python
import sqlite3
import pandas as pd
con = sqlite3.connect("bazaar.db")
q = lambda sql: pd.read_sql(sql, con)
print(q("""
WITH orders AS (
    SELECT order_id, city, category, order_date,
           quantity * unit_price * (1 - discount) AS revenue
    FROM sales
),
city_rev AS (
    SELECT city, SUM(revenue) AS revenue FROM orders GROUP BY city
)
SELECT city, ROUND(revenue / 100000.0, 2) AS revenue_lakh
FROM city_rev
WHERE revenue > (SELECT AVG(revenue) FROM city_rev)      -- alias usable now!
ORDER BY revenue DESC
"""))
city revenue_lakh 0 Mumbai 10.43 1 Bengaluru 9.64 2 Delhi 9.46 3 Kolkata 8.40

Window functions: rows बनाए रखें

GROUP BY

  • Rows समेटता है: हर group की एक row
  • हर department का SUM(salary)
  • pandas agg जैसा

Window: OVER (PARTITION BY …)

  • हर row रखता है और group value जोड़ता है
  • हर employee + department total
  • pandas transform / rank / shift जैसा
Python
import sqlite3
import pandas as pd
con = sqlite3.connect("bazaar.db")
q = lambda sql: pd.read_sql(sql, con)
print(q("""
SELECT name, dept_id, salary,
       ROUND(AVG(salary) OVER (PARTITION BY dept_id))            AS dept_avg,
       salary - ROUND(AVG(salary) OVER (PARTITION BY dept_id))   AS vs_avg,
       ROUND(100.0 * salary / SUM(salary) OVER (PARTITION BY dept_id), 1) AS share_pct
FROM employees
WHERE dept_id IN (10, 30)
ORDER BY dept_id, salary DESC
"""))
name dept_id salary dept_avg vs_avg share_pct 0 Vihaan 10 78000 57500.0 20500.0 13.6 1 Yash 10 74000 57500.0 16500.0 12.9 2 Pooja 10 65000 57500.0 7500.0 11.3 3 Isha 10 61000 57500.0 3500.0 10.6 4 Nisha 10 57000 57500.0 -500.0 9.9 5 Sara 10 53000 57500.0 -4500.0 9.2 6 Om 10 50000 57500.0 -7500.0 8.7 7 Ananya 10 49000 57500.0 -8500.0 8.5 8 Arjun 10 46000 57500.0 -11500.0 8.0 9 Kavya 10 42000 57500.0 -15500.0 7.3 10 Zoya 30 62000 52800.0 9200.0 23.5 11 Harsh 30 57000 52800.0 4200.0 21.6 12 Siddharth 30 52000 52800.0 -800.0 19.7 13 Dev 30 51000 52800.0 -1800.0 19.3 14 Aisha 30 42000 52800.0 -10800.0 15.9

Ranking functions

Python
import sqlite3
import pandas as pd
con = sqlite3.connect("bazaar.db")
q = lambda sql: pd.read_sql(sql, con)
print(q("""
WITH team(name, salary) AS (
    VALUES ('Aman', 90000), ('Riya', 90000), ('Om', 85000),
           ('Zoya', 80000), ('Kabir', 80000), ('Isha', 75000)
)
SELECT name, salary,
       ROW_NUMBER() OVER (ORDER BY salary DESC) AS row_num,
       RANK()       OVER (ORDER BY salary DESC) AS rnk,
       DENSE_RANK() OVER (ORDER BY salary DESC) AS dense_rnk
FROM team
"""))
name salary row_num rnk dense_rnk 0 Aman 90000 1 1 1 1 Riya 90000 2 1 1 2 Om 85000 3 3 2 3 Zoya 80000 4 4 3 4 Kabir 80000 5 4 3 5 Isha 75000 6 6 4
🧠 बराबरी (Ties)

बराबर salaries पर ROW_NUMBER फिर भी 1, 2, 3 देता है; RANK 1, 1, 3 (छोड़ता है); DENSE_RANK 1, 1, 2 (कोई gap नहीं)। Interviewers दूसरी सबसे बड़ी salary पूछना पसंद करते हैं – DENSE_RANK लें।

Python
import sqlite3
import pandas as pd
con = sqlite3.connect("bazaar.db")
q = lambda sql: pd.read_sql(sql, con)
print(q("""
WITH r AS (
    SELECT city, product, ROUND(SUM(quantity * unit_price * (1 - discount))) AS revenue,
           ROW_NUMBER() OVER (PARTITION BY city ORDER BY SUM(quantity * unit_price * (1 - discount)) DESC) AS rn
    FROM sales
    GROUP BY city, product
)
SELECT city, product, revenue FROM r WHERE rn <= 2 AND city IN ('Delhi', 'Mumbai', 'Patna', 'Jaipur')
ORDER BY city, rn
"""))
city product revenue 0 Delhi Mobile Phone 234751.0 1 Delhi Laptop 193238.0 2 Jaipur Sneakers 39827.0 3 Jaipur Mobile Phone 31546.0 4 Mumbai Laptop 412270.0 5 Mumbai Mobile Phone 175879.0 6 Patna Laptop 129379.0 7 Patna Mobile Phone 116078.0
Python
import sqlite3
import pandas as pd
con = sqlite3.connect("bazaar.db")
q = lambda sql: pd.read_sql(sql, con)
print(q("""
WITH ranked AS (
    SELECT d.dept_name, e.name, e.salary,
           DENSE_RANK() OVER (PARTITION BY e.dept_id ORDER BY e.salary DESC) AS r
    FROM employees e JOIN departments d ON e.dept_id = d.dept_id
)
SELECT dept_name, name, salary FROM ranked WHERE r = 2      -- 2nd highest per department
"""))
dept_name name salary 0 Sales Yash 74000 1 Data Science Tanvi 135000 2 HR Harsh 57000

Running totals और moving averages

Python
import sqlite3
import pandas as pd
con = sqlite3.connect("bazaar.db")
q = lambda sql: pd.read_sql(sql, con)
m = q("""
WITH monthly AS (
    SELECT strftime('%Y-%m', order_date) AS month,
           SUM(quantity * unit_price * (1 - discount)) / 100000.0 AS rev
    FROM sales GROUP BY month
)
SELECT month, ROUND(rev, 2) AS revenue_lakh,
       ROUND(SUM(rev) OVER (ORDER BY month), 2)                                   AS cumulative,
       ROUND(AVG(rev) OVER (ORDER BY month ROWS BETWEEN 2 PRECEDING AND CURRENT ROW), 2) AS moving_avg_3m
FROM monthly
""")
print(m.to_string(index=False))
month revenue_lakh cumulative moving_avg_3m 2024-01 7.03 7.03 7.03 2024-02 5.24 12.27 6.13 2024-03 5.90 18.16 6.05 2024-04 4.32 22.48 5.15 2024-05 4.08 26.56 4.76 2024-06 5.02 31.58 4.47 2024-07 4.44 36.01 4.51 2024-08 4.92 40.93 4.79 2024-09 4.45 45.39 4.60 2024-10 8.61 54.00 5.99 2024-11 10.00 63.99 7.69 2024-12 4.80 68.80 7.80
Python
import sqlite3
import pandas as pd
con = sqlite3.connect("bazaar.db")
q = lambda sql: pd.read_sql(sql, con)
import matplotlib.pyplot as plt
m = q("""
WITH monthly AS (SELECT strftime('%m', order_date) AS month, SUM(quantity * unit_price * (1 - discount)) / 100000.0 AS rev
                 FROM sales GROUP BY month)
SELECT month, rev, SUM(rev) OVER (ORDER BY month) AS cum FROM monthly
""")
fig, ax = plt.subplots(figsize=(9, 3.8))
ax.bar(m["month"], m["rev"], color="#c7d2fe", label="monthly")
ax.plot(m["month"], m["cum"], color="#4f46e5", marker="o", label="cumulative")
ax.axhline(65, color="#dc2626", ls="--", label="annual target ₹65 lakh")
cross = m.loc[m["cum"] >= 65, "month"].iloc[0]
ax.set_title(f"Annual target crossed in month {cross}"); ax.set_ylabel("₹ lakh"); ax.legend()
plt.show()
Monthly revenue bars और annual target पार करती cumulative line
SUM() OVER (ORDER BY month) cumulative line सीधे SQL में बनाता है।

LAG और LEAD: growth

Python
import sqlite3
import pandas as pd
con = sqlite3.connect("bazaar.db")
q = lambda sql: pd.read_sql(sql, con)
print(q("""
WITH monthly AS (
    SELECT strftime('%Y-%m', order_date) AS month,
           SUM(quantity * unit_price * (1 - discount)) AS rev
    FROM sales GROUP BY month
)
SELECT month,
       ROUND(rev / 1e5, 2)                               AS rev_lakh,
       ROUND(LAG(rev) OVER (ORDER BY month) / 1e5, 2)    AS prev_lakh,
       ROUND(100.0 * (rev - LAG(rev) OVER (ORDER BY month)) / LAG(rev) OVER (ORDER BY month), 1) AS mom_growth_pct
FROM monthly
""").to_string(index=False))
month rev_lakh prev_lakh mom_growth_pct 2024-01 7.03 NaN NaN 2024-02 5.24 7.03 -25.5 2024-03 5.90 5.24 12.6 2024-04 4.32 5.90 -26.8 2024-05 4.08 4.32 -5.6 2024-06 5.02 4.08 23.0 2024-07 4.44 5.02 -11.5 2024-08 4.92 4.44 10.8 2024-09 4.45 4.92 -9.4 2024-10 8.61 4.45 93.3 2024-11 10.00 8.61 16.1 2024-12 4.80 10.00 -52.0

NTILE: segments

Python
import sqlite3
import pandas as pd
con = sqlite3.connect("bazaar.db")
q = lambda sql: pd.read_sql(sql, con)
print(q("""
WITH c AS (
    SELECT city, SUM(quantity * unit_price * (1 - discount)) AS rev FROM sales GROUP BY city
)
SELECT city, ROUND(rev) AS revenue,
       NTILE(3) OVER (ORDER BY rev DESC) AS tier,
       ROUND(PERCENT_RANK() OVER (ORDER BY rev), 2) AS pct_rank
FROM c ORDER BY rev DESC
"""))
city revenue tier pct_rank 0 Mumbai 1043230.0 1 1.0 1 Bengaluru 964020.0 1 0.9 2 Delhi 945592.0 1 0.8 3 Kolkata 839885.0 1 0.7 4 Hyderabad 572190.0 2 0.6 5 Chennai 545479.0 2 0.5 6 Patna 459941.0 2 0.4 7 Ahmedabad 450612.0 2 0.3 8 Pune 403766.0 3 0.2 9 Lucknow 394912.0 3 0.1 10 Jaipur 260155.0 3 0.0
SQL windowpandas
AVG(x) OVER (PARTITION BY g)df.groupby("g")["x"].transform("mean")
ROW_NUMBER() OVER (PARTITION BY g ORDER BY x DESC)df.groupby("g")["x"].rank(method="first", ascending=False)
SUM(x) OVER (ORDER BY t)df["x"].cumsum()
AVG(x) OVER (… ROWS 2 PRECEDING)df["x"].rolling(3).mean()
LAG(x)df["x"].shift(1)
NTILE(4)pd.qcut(df.x, 4)

🏅 आज के Job Skills

✔ CTEs से queries का ढाँचा✔ हर row पर group values के लिए PARTITION BY✔ हर group का rank और top-N✔ Running totals, moving averages और growth निकालना

Practice tasks

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

Task 1: हर region में शहरों को revenue के हिसाब से rank दें (1 = सबसे ज़्यादा)।
Solution देखें
Python
import sqlite3
import pandas as pd
con = sqlite3.connect("bazaar.db")
q = lambda sql: pd.read_sql(sql, con)
print(q("""WITH c AS (SELECT region, city, SUM(quantity*unit_price*(1-discount)) AS rev FROM sales GROUP BY region, city)
SELECT region, city, ROUND(rev) AS rev, RANK() OVER (PARTITION BY region ORDER BY rev DESC) AS rnk FROM c ORDER BY region, rnk"""))
region city rev rnk 0 East Kolkata 839885.0 1 1 East Patna 459941.0 2 2 North Delhi 945592.0 1 3 North Lucknow 394912.0 2 4 North Jaipur 260155.0 3 5 South Bengaluru 964020.0 1 6 South Hyderabad 572190.0 2 7 South Chennai 545479.0 3 8 West Mumbai 1043230.0 1 9 West Ahmedabad 450612.0 2 10 West Pune 403766.0 3
Task 2: Company की तीसरी सबसे ज़्यादा salary निकालें (ties संभालें)।
Solution देखें
Python
import sqlite3
import pandas as pd
con = sqlite3.connect("bazaar.db")
q = lambda sql: pd.read_sql(sql, con)
print(q("""SELECT DISTINCT salary FROM (SELECT salary, DENSE_RANK() OVER (ORDER BY salary DESC) AS r FROM employees) WHERE r = 3"""))
salary 0 122000
Task 3: October 2024 के weekly orders running total के साथ दिखाएँ।
Solution देखें
Python
import sqlite3
import pandas as pd
con = sqlite3.connect("bazaar.db")
q = lambda sql: pd.read_sql(sql, con)
print(q("""WITH w AS (SELECT strftime('%W', order_date) AS week, COUNT(*) AS orders FROM sales
WHERE order_date BETWEEN '2024-10-01' AND '2024-10-31' GROUP BY week)
SELECT week, orders, SUM(orders) OVER (ORDER BY week) AS running FROM w"""))
week orders running 0 40 48 48 1 41 50 98 2 42 56 154 3 43 64 218 4 44 31 249

Quick quiz

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

1. Window functions GROUP BY से कैसे अलग हैं?
वे rows की window पर निकाली value जोड़ते हैं।
2. Salaries 100, 100, 90: RANK देता है…
RANK ties के बाद छोड़ता है।
3. LAG(revenue) क्या लौटाता है?
OVER clause के क्रम में।
4. PARTITION BY dept_id मतलब…
groupby जैसा, बिना समेटे।
5. CTE किससे शुरू होता है?
WITH name AS (…) SELECT …

Interview corner

💼 Interview Corner

हर category के revenue के हिसाब से top 3 products की query लिखिए।

WITH r AS (SELECT category, product, SUM(rev) AS rev, ROW_NUMBER() OVER (PARTITION BY category ORDER BY SUM(rev) DESC) AS rn FROM sales GROUP BY category, product) SELECT * FROM r WHERE rn <= 3;

SQL में month-over-month growth कैसे निकालेंगे?

CTE में हर महीने की एक row बनाएँ, फिर (rev − LAG(rev) OVER (ORDER BY month)) / LAG(rev) OVER (ORDER BY month) × 100।

सारांश

  • WITH … AS पढ़ने में आसान step-by-step queries बनाता है।
  • OVER (PARTITION BY … ORDER BY …) rows समेटे बिना group values जोड़ता है।
  • Rankings और top-N के लिए ROW_NUMBER/RANK/DENSE_RANK।
  • Running totals के लिए SUM OVER, growth के लिए LAG, tiers के लिए NTILE।
➡️ आगे

Day 35: SQL + pandas project – छोटा sales database बनाकर 8 business सवालों के पूरे जवाब दें।

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

क्या MySQL window functions support करता है?
हाँ, MySQL 8.0 से। PostgreSQL, SQL Server, BigQuery और Snowflake सभी करते हैं।
क्या WHERE में window function पर filter कर सकते हैं?
नहीं – window functions WHERE के बाद चलते हैं। CTE/sub-query में निकालें और बाहर filter करें (top-N example की तरह)।
← Data Science (60 Days)
🔗

Share this topic with a friend

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

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

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

अगला topicदिन 35: Project: SQL + Pandas HR Analytics आपकी प्रगति: 34 / 60 (57%) · Lesson 35 / 60 आगे बढ़ें →

यह lesson कैसा लगा?

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

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

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

▶ CodingEasily on YouTube

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

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

▶ Channel subscribe करें