Tips 17–20: Aggregations & Subqueries
Speed up heavy aggregations with columnstore and pre-aggregated tables, choose correctly between JOIN, EXISTS and IN, and simplify repeated logic with CTEs.
Reports like "total runs per team per season" run aggregations over huge tables, and analysts love subqueries. These four tips keep both fast.
Tip 17 — Columnstore for heavy aggregates
Use columnstore indexes for large analytic aggregations.
sql
-- ❌ Bad: SUM over 100M rows stored as rowstore - reads every column of every row
SELECT team, SUM(runs) FROM ball_by_ball GROUP BY team;
-- ✅ Good
CREATE CLUSTERED COLUMNSTORE INDEX CCI_ball_by_ball ON ball_by_ball;After this, the same SUM reads only the compressed team and runs segments in batch mode. (See the Rowstore vs Columnstore topic.)
Tip 18 — Pre-aggregate results
Store summaries in a separate table for fast reporting, instead of recalculating
SUM()on every report run.
sql
-- ❌ Bad: dashboard recalculates totals from raw innings every page load
SELECT team, SUM(runs) FROM innings GROUP BY team;
-- ✅ Good (SQL Server): build a summary table once (e.g. nightly)
SELECT team, COUNT(*) AS innings_played, SUM(runs) AS total_runs
INTO team_totals
FROM innings
GROUP BY team;SELECT … INTO creates the new table and fills it in one step (SQL Server only). The portable form is CREATE TABLE + INSERT INTO … SELECT:
| team | innings_played | total_runs |
|---|---|---|
| RR | 3 | 239 |
| RCB | 3 | 214 |
| MI | 3 | 121 |
| CSK | 3 | 71 |
The report now reads 4 rows instead of aggregating millions. The trade-off: the summary is only as fresh as its last refresh. (SQL Server also offers indexed views, which keep a summary automatically up to date.)
Tip 19 — JOIN, EXISTS or IN?
Know when to use each, and avoid IN with very large lists.
| Use | When | Why |
|---|---|---|
| JOIN | you need columns from both tables | combines data — but can multiply rows (1 player × 3 innings) |
| EXISTS | you only need to know whether a match exists | stops at the first match; no duplicates; NULL-safe |
| IN | a short, fixed list of values, or a small subquery | most readable for small lists |
sql
-- ❌ Bad: a huge literal list
SELECT name FROM players WHERE id IN (1, 2, 3, /* ... 50,000 ids ... */ 99999);
-- ✅ Good: check existence against a table
SELECT p.name
FROM players AS p
WHERE EXISTS (
SELECT 1
FROM innings AS i
WHERE i.player = p.name
);| name |
|---|
| Virat Kohli |
| Rohit Sharma |
| Jos Buttler |
| MS Dhoni |
The same question with INNER JOIN innings returns 12 rows (each player once per innings) — you would need DISTINCT to fix it, which costs a sort. EXISTS returns each player once with no extra work.
Trap — NOT IN with NULLs:
WHERE x NOT IN (subquery)returns no rows at all if the subquery returns even one NULL. In our data, David Warner's profile hasteam = NULL, soSELECT name FROM players WHERE team NOT IN (SELECT team FROM player_profiles WHERE country IN ('Australia','England'))returns 0 rows, while theNOT EXISTSversion correctly returns 5. Prefer NOT EXISTS.
Tip 20 — Simplify with a CTE
Use a CTE (
WITH … AS) to remove repeated logic and make the query readable.
sql
-- ❌ Bad: the same subquery pasted twice
SELECT team, total_runs
FROM (SELECT team, SUM(runs) AS total_runs FROM innings GROUP BY team) AS x
WHERE total_runs > (
SELECT AVG(total_runs)
FROM (SELECT team, SUM(runs) AS total_runs FROM innings GROUP BY team) AS y
);
-- ✅ Good: define it once
WITH team_totals AS (
SELECT team, SUM(runs) AS total_runs
FROM innings
GROUP BY team
)
SELECT team, total_runs
FROM team_totals
WHERE total_runs > (SELECT AVG(total_runs) FROM team_totals)
ORDER BY total_runs DESC;| team | total_runs |
|---|---|
| RR | 239 |
| RCB | 214 |
The average team total is about 161, so only RR and RCB are above it.
Note: in SQL Server a CTE is not stored/cached — referencing it twice may run its query twice. The main gain is readability and fewer copy-paste bugs. If a heavy CTE is reused many times, materialise it into a
#temptable.
Common mistakes
- ❌
JOIN+DISTINCTto answer "does a match exist?" ✅EXISTS. - ❌
NOT INagainst a nullable column. ✅NOT EXISTS. - ❌ A pre-aggregated table nobody refreshes. ✅ Schedule the refresh (SQL Agent job) and show "last updated".
SQL Server vs MySQL / PostgreSQL
| Task | SQL Server | MySQL | PostgreSQL |
|---|---|---|---|
| Create table from query | SELECT … INTO new_table FROM … | CREATE TABLE new_table AS SELECT … | CREATE TABLE new_table AS SELECT … |
| Auto-maintained summary | indexed view | — | materialized view (manual REFRESH) |
Interview tip
Q: EXISTS or IN — which is faster? A: For a subquery, modern optimizers often produce the same plan. The real differences: EXISTS stops at the first match and handles NULLs correctly with NOT EXISTS, while NOT IN returns nothing if the subquery has a NULL. Use IN for short fixed lists, EXISTS for existence checks against tables, and JOIN when you need columns from both sides.
Key points to remember
- Tip 17: use columnstore indexes for heavy aggregations over very large tables.
- Tip 18: pre-aggregate into a summary table (SELECT … INTO in SQL Server) so reports don't recompute SUM() every time.
- Tip 19: JOIN when you need columns from both tables, EXISTS to test for existence, IN for short fixed lists — avoid huge IN lists.
- A JOIN can multiply rows (one per match); EXISTS returns each outer row once.
- NOT IN returns no rows if the subquery contains a NULL — prefer NOT EXISTS.
- Tip 20: use a CTE to define repeated logic once and make the query readable.
Try it yourself
Tip 18: SQL Server pre-aggregation with SELECT … INTO
-- Build (or rebuild nightly) a summary table
SELECT
team,
COUNT(*) AS innings_played,
SUM(runs) AS total_runs
INTO team_totals
FROM innings
GROUP BY team;
-- Reports read the small table
SELECT * FROM team_totals ORDER BY total_runs DESC;SQL Server-specific — run this in SQL Server (SSMS / Azure Data Studio).
Try it: pre-aggregate with CREATE TABLE + INSERT … SELECT
Runs on a fresh IPL practice database in your browser · edit the query and press Ctrl + Enter
Tip 19: EXISTS vs JOIN vs the NOT IN trap
Runs on a fresh IPL practice database in your browser · edit the query and press Ctrl + Enter
Practice: CTE for above-average teams
Runs on a fresh IPL practice database in your browser · edit the query and press Ctrl + Enter
Revision Flashcards
Tap a card to flip it and see the answer.
