Views, CTEs & Recursive Queries
View, CTE ও recursive query
1. The Quest for Reusable SQL
Real production queries can run to hundreds of lines. Without a way to break them into named pieces, those queries become unreadable, untestable, and impossible to maintain. SQL gives us three tools, in roughly increasing power:
- Views — saved queries, stored in the database catalog. Anyone with permission can
SELECTfrom them as if they were tables. - CTEs (the
WITHclause) — named subqueries scoped to a single statement. - Recursive CTEs — CTEs that reference themselves, letting SQL solve hierarchy, graph traversal, and series-generation problems that no flat join can.
View হলো database-এ রাখা function, CTE হলো এক query-র ভেতরে local variable, আর recursive CTE হলো লুপ — কিন্তু set-এর ভাষায় লেখা।
2. Views — Saved Queries That Look Like Tables
A view is created with CREATE VIEW name AS <SELECT...>. It stores no data — it stores the definition. Every time you query the view, the engine substitutes its definition into your query and runs the combined thing.
CREATE VIEW name AS SELECT ... দিয়ে। View-তে কোনো data থাকে না — শুধু query-র সংজ্ঞা থাকে। যখনই আপনি view থেকে SELECT করেন, database সেই সংজ্ঞা বসিয়ে query চালায়।
-- Define a view of CSE students with GPA >= 3.7
CREATE VIEW cse_honours AS
SELECT id, name, gpa
FROM students
WHERE dept = 'CSE' AND gpa >= 3.7;
-- Use the view exactly like a table
SELECT * FROM cse_honours ORDER BY gpa DESC;
To remove a view, use DROP VIEW name. To redefine, drop and recreate (most engines also accept CREATE OR REPLACE VIEW; SQLite uses the drop-and-create pattern).
2.1 — Why use views?
- Encapsulation: business rules ("what counts as 'honours'?") live in one place.
- Security: hide sensitive columns and grant
SELECTonly on the view. - Compatibility: rename the underlying table without breaking client code.
2.2 — Read-only vs updatable views
Some views, especially those built on a single table without aggregation, are updatable — you can INSERT, UPDATE, or DELETE through them and the engine pushes the change to the underlying table. Views with JOIN, GROUP BY, DISTINCT, set ops, or window functions are typically read-only.
| Feature | View | CTE |
|---|---|---|
| Persists in DB? | Yes — until DROPped | No — only inside one statement |
| Reusable across queries? | Yes | No |
| Recursion? | Indirect (call a recursive CTE) | Yes — direct |
| Permissioned? | Yes — own GRANT | No — uses caller's perms |
3. CTEs — Local Subqueries with Names
A Common Table Expression (CTE) is introduced with the WITH keyword. It gives a name to a subquery that you can then reference one or more times in the main query — much like a local variable in a programming language.
WITH name AS (SELECT ...) দিয়ে subquery-কে একটি নাম দেওয়া হয়, এরপর সেই নাম ব্যবহার করে মূল query লেখা যায়। এটি statement-এর ভেতরেই কেবল live থাকে — query শেষ হলেই হারিয়ে যায়।
WITH spend AS (
SELECT cid, SUM(amount) AS total_tk
FROM orders
GROUP BY cid
)
SELECT c.name, s.total_tk
FROM spend s
JOIN customers c ON c.id = s.cid
WHERE s.total_tk > 500
ORDER BY s.total_tk DESC;
3.1 — Chained CTEs
You can declare multiple CTEs in one WITH block, and a later CTE may refer to an earlier one. This is the gold-standard way to structure complex analytical queries:
WITH
monthly AS (
SELECT cid, substr(ts,1,7) AS ym, SUM(amount) AS m_tk
FROM orders
GROUP BY cid, substr(ts,1,7)
),
peaks AS (
SELECT cid, MAX(m_tk) AS peak
FROM monthly
GROUP BY cid
)
SELECT c.name, p.peak AS peak_month_tk
FROM peaks p
JOIN customers c ON c.id = p.cid
ORDER BY p.peak DESC;
SELECT is the "presentation" step. This is much more debuggable than one giant nested query.
Chained CTE-গুলোকে একটি pipeline-এর মতো পড়ুন — প্রতিটি ধাপ আগেরটার ওপর কাজ করে। বিশাল nested query-র চেয়ে এটি অনেক সহজে পরীক্ষা ও debug করা যায়।
4. Recursive CTEs — Letting SQL Loop
A recursive CTE consists of two parts joined by UNION ALL:
- Anchor — the base case, a non-recursive query that produces the starting set.
- Recursive step — a query that references the CTE itself, producing the next set from the current one.
The engine evaluates the anchor, then repeatedly applies the recursive step using the rows produced in the previous iteration as input, until no new rows are produced. The accumulated rows become the CTE's value.
UNION ALL দিয়ে জোড়া হয়। যতক্ষণ না নতুন row আসা বন্ধ হয়, ততক্ষণ এটি চলতে থাকে। SQLite ৩.৮.৩ থেকে এটি সমর্থন করে।
4.1 — Generating numbers 1..10
WITH RECURSIVE n(i) AS (
SELECT 1 -- anchor
UNION ALL
SELECT i + 1 FROM n WHERE i < 10 -- recursive step
)
SELECT i FROM n;
4.2 — Walking an org chart
WITH RECURSIVE org(id, name, manager_id, level, path) AS (
-- anchor: the CEO has no manager
SELECT id, name, manager_id, 0, name
FROM employees
WHERE manager_id IS NULL
UNION ALL
-- step: each employee is one level below their manager
SELECT e.id, e.name, e.manager_id,
o.level + 1,
o.path || ' > ' || e.name
FROM employees e
JOIN org o ON e.manager_id = o.id
)
SELECT level, name, path FROM org ORDER BY level, name;
4.3 — Traversing a category tree
The same pattern works for any parent-child structure: file systems, comment threads, product categories, family trees.
WITH RECURSIVE tree(id, name, depth, label) AS (
SELECT id, name, 0, name
FROM category WHERE parent IS NULL
UNION ALL
SELECT c.id, c.name, t.depth+1,
printf('%s/%s', t.label, c.name)
FROM category c
JOIN tree t ON c.parent = t.id
)
SELECT depth, label FROM tree ORDER BY label;
WHERE clause on a depth counter.
data-তে cycle থাকলে বা termination না থাকলে recursive CTE অনন্তকাল চলবে। সবসময় depth-limit বা cycle-detection রাখুন।
5. Views & CTEs — Performance Realities
A view is just a query definition; running SELECT ... FROM v WHERE ... usually inlines the view and lets the optimizer push your filter down. Views are essentially free.
CTEs are subtler. In SQLite, a CTE is materialized by default if it's referenced more than once or if it's recursive. That means it's computed into a temporary table once. If it's referenced once and is non-recursive, the engine usually inlines it. You can force materialization with the MATERIALIZED hint or prevent it with NOT MATERIALIZED in newer SQLite builds.
MATERIALIZED/NOT MATERIALIZED hint দিয়ে নিয়ন্ত্রণ করা যায়।
✅ View — Best for (সেরা ব্যবহার)
- Cross-query reuse and permission control
- Hiding sensitive columns / business rules
- Stable interface above evolving tables
✅ CTE — Best for (সেরা ব্যবহার)
- Readability inside one big query
- Recursion / hierarchy traversal
- Multi-step transformations as a pipeline
6. Cheat Sheet (এক নজরে)
| Construct | Syntax | Lifetime |
|---|---|---|
| Create a view | CREATE VIEW v AS SELECT ...; | Until DROP VIEW |
| Drop a view | DROP VIEW v; | — |
| One CTE | WITH t AS (...) SELECT ...; | One statement |
| Chained CTEs | WITH a AS (...), b AS (... a ...) SELECT ...; | One statement |
| Recursive CTE | WITH RECURSIVE r(...) AS (anchor UNION ALL step) SELECT ...; | One statement |
7. Practice Problems
16 problems on views, simple CTEs, chained CTEs, and recursion.
- Create a view
top_studentswith students having GPA >= 3.8, then query it.GPA >= 3.8 student-দের একটি view বানিয়ে query করুন।✨ Show Answer
a1.sqlCREATE VIEW top_students AS SELECT id,name,gpa FROM students WHERE gpa>=3.8; SELECT * FROM top_students ORDER BY gpa DESC; - Drop the view from problem 1.আগের view-টি drop করুন।
✨ Show Answer
a2.sqlDROP VIEW top_students; SELECT name FROM sqlite_master WHERE type='view'; - Use a CTE to compute total spend per customer, then list those above 500 BDT.CTE দিয়ে customer-wise total ও ৫০০-এর বেশি ফিল্টার।
✨ Show Answer
a3.sqlWITH spend AS ( SELECT cid,SUM(amount) AS tot FROM orders GROUP BY cid ) SELECT c.name,s.tot FROM spend s JOIN customers c ON c.id=s.cid WHERE s.tot>500; - Generate the integers 1 to 20 with a recursive CTE.Recursive CTE-তে ১ থেকে ২০ পর্যন্ত সংখ্যা তৈরি করুন।
✨ Show Answer
a4.sqlWITH RECURSIVE n(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM n WHERE i<20 ) SELECT i FROM n; - Generate every date from 2025-01-01 to 2025-01-10 using recursion.2025-01-01 থেকে 2025-01-10 পর্যন্ত প্রতিটি তারিখ।
✨ Show Answer
a5.sqlWITH RECURSIVE d(day) AS ( SELECT date('2025-01-01') UNION ALL SELECT date(day,'+1 day') FROM d WHERE day < date('2025-01-10') ) SELECT day FROM d; - List every employee's chain of command up to the CEO using a recursive CTE.প্রত্যেক employee-এর CEO পর্যন্ত chain তৈরি করুন।
✨ Show Answer
a6.sqlWITH RECURSIVE chain(id,name,path) AS ( SELECT id,name,name FROM employees UNION ALL SELECT c.id,c.name,c.path||' -> '||m.name FROM chain c JOIN employees e ON e.id=c.id JOIN employees m ON m.id=e.manager_id ) SELECT name,path FROM chain WHERE path LIKE '%Karim' ORDER BY name; - Walk the category tree: list every leaf category along with its full path.Category tree-এ প্রতিটি leaf-এর সম্পূর্ণ path।
✨ Show Answer
a7.sqlWITH RECURSIVE t(id,name,label) AS ( SELECT id,name,name FROM category WHERE parent IS NULL UNION ALL SELECT c.id,c.name,t.label||'/'||c.name FROM category c JOIN t ON c.parent=t.id ) SELECT label FROM t WHERE NOT EXISTS (SELECT 1 FROM category WHERE parent=t.id) ORDER BY label; - Compute Fibonacci numbers F(1)..F(15) with a recursive CTE.Recursive CTE দিয়ে Fibonacci F(1) থেকে F(15)।
✨ Show Answer
a8.sqlWITH RECURSIVE fib(n,a,b) AS ( SELECT 1,0,1 UNION ALL SELECT n+1,b,a+b FROM fib WHERE n<15 ) SELECT n,b AS fib FROM fib; - Use chained CTEs: monthly totals → yearly totals from
orders.Chained CTE: মাসিক → বার্ষিক total।✨ Show Answer
a9.sqlWITH monthly AS ( SELECT substr(ts,1,7) AS ym, SUM(amount) AS m FROM orders GROUP BY 1 ), yearly AS ( SELECT substr(ym,1,4) AS y, SUM(m) AS total FROM monthly GROUP BY 1 ) SELECT * FROM yearly ORDER BY y; - Create a view that hides salary but shows everything else of
employees.salary বাদ দিয়েemployees-এর বাকি সব কিছু দেখায় — এমন view।✨ Show Answer
a10.sqlCREATE VIEW employees_public AS SELECT id,name,dept FROM employees; SELECT * FROM employees_public; - Find every descendant (direct or indirect) of category id = 1 with a recursive CTE.id=1 category-র সব descendant।
✨ Show Answer
a11.sqlWITH RECURSIVE desc(id,name) AS ( SELECT id,name FROM category WHERE parent=1 UNION ALL SELECT c.id,c.name FROM category c JOIN desc d ON c.parent=d.id ) SELECT * FROM desc ORDER BY id; - In one sentence, what is the difference between a view and a CTE?View ও CTE-এর মূল পার্থক্য কী?
✨ Show Answer
Answer: A view is a saved query that persists in the database catalog and is reusable across statements; a CTE is a named subquery that lives only inside the single statement that defines it.
View database-এ সংরক্ষিত — যেকোনো query থেকে ডাকা যায়। CTE শুধু সেই একটি statement-এর ভেতরেই থাকে।
- Use a CTE to identify customers whose biggest order exceeds 2× their average.CTE দিয়ে — যেসব customer-এর সর্বোচ্চ order তাদের গড়ের ২ গুণ ছাড়িয়েছে।
✨ Show Answer
a13.sqlWITH stats AS ( SELECT cid,AVG(amount) AS avg_a,MAX(amount) AS max_a FROM orders GROUP BY cid ) SELECT * FROM stats WHERE max_a > 2*avg_a; - Generate the multiplication table for 7 (1×7 to 10×7) with recursion.৭-এর নামতা ১ থেকে ১০ — recursive CTE-তে।
✨ Show Answer
a14.sqlWITH RECURSIVE t(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM t WHERE i<10 ) SELECT i,i*7 AS result FROM t; - Why might a recursive CTE without a termination condition cause an infinite loop?termination না থাকলে recursive CTE-তে infinite loop কেন হয়?
✨ Show Answer
Answer: The engine keeps applying the recursive step using the previous iteration's rows. If the step always produces at least one new row (or the data has a cycle and you don't track visited rows), the iteration never empties — so the engine keeps adding rows until memory or a built-in safety limit aborts it.
প্রতিটি iteration-এ নতুন row আসতে থাকলে, বা data-তে cycle থাকলে, পুনরাবৃত্তি বন্ধ হয় না — memory শেষ না হওয়া পর্যন্ত চলতে থাকে।
- Build an org chart view
org_with_levelusing a recursive CTE wrapped in a view.Recursive CTE-কে view-তে মুড়েorg_with_levelবানান।✨ Show Answer
a16.sqlCREATE VIEW org_with_level AS WITH RECURSIVE o(id,name,level) AS ( SELECT id,name,0 FROM employees WHERE manager_id IS NULL UNION ALL SELECT e.id,e.name,o.level+1 FROM employees e JOIN o ON e.manager_id=o.id ) SELECT * FROM o; SELECT * FROM org_with_level ORDER BY level,name;
Summary — Module 19
A view is a saved query that persists in the catalog — perfect for encapsulation, security, and stable interfaces. A CTE (the WITH clause) is a named, statement-scoped subquery that turns one giant nested query into a readable pipeline of named steps.
A recursive CTE = anchor + recursive step joined by UNION ALL — it is how SQL handles hierarchies, graph traversal, and series generation. Always include a termination condition; cycles in the data require explicit cycle detection.
WITH-এর সাহায্যে নাম দেওয়া subquery, এক statement-এর ভেতর pipeline-এর মতো কাজ করে। Recursive CTE (anchor + step) দিয়ে hierarchy বা series তৈরি করা যায় — কিন্তু termination শর্ত অবশ্যই রাখতে হবে, না হলে অনন্ত লুপ।