Cheat sheetSQLQuery order, joins, aggregation, window functions, CTEs, dates, NULL semantics, analytics patterns, and query safety. 'TEST');","id":"bl_05_002"},{"type":"keyvalue","items":[{"term":"Parentheses","desc":"Always group mixed AND / OR conditions explicitly."},{"term":"BETWEEN","desc":"Inclusive at both ends; prefer half-open ranges for timestamps."},{"term":"DISTINCT","desc":"Deduplicates output rows; it can hide data-quality problems."}],"id":"bl_05_003"}]},{"id":"card_05_002","type":"card","x":0,"y":1042,"w":560,"h":374,"z":13,"title":"CASE Expressions","sectionNumber":"3","accent":"accent","bg":"surface","showHeader":true,"blocks":[{"type":"code","language":"sql","code":"SELECT order_id,\n CASE\n WHEN revenue >= 1000 THEN 'high'\n WHEN revenue >= 100 THEN 'mid'\n ELSE 'low'\n END AS segment\nFROM orders;","id":"bl_05_004"},{"type":"keyvalue","items":[{"term":"First match wins","desc":"Order WHEN branches from most to least specific."},{"term":"Missing ELSE","desc":"Unmatched rows become NULL."}],"id":"bl_05_005"}]},{"id":"card_05_003","type":"card","x":0,"y":1440,"w":560,"h":415,"z":14,"title":"Aggregation","sectionNumber":"4","accent":"accent","bg":"surface","showHeader":true,"blocks":[{"type":"code","language":"sql","code":"SELECT country,\n COUNT(*) AS orders,\n COUNT(DISTINCT user_id) AS buyers,\n SUM(revenue) AS revenue,\n AVG(revenue) AS aov\nFROM orders\nGROUP BY country\nHAVING COUNT(*) >= 100\nORDER BY revenue DESC;","id":"bl_05_006"},{"type":"keyvalue","items":[{"term":"COUNT(*) vs COUNT(col)","desc":"COUNT(col) skips NULLs."},{"term":"Conditional aggregation","desc":"`SUM(CASE WHEN … THEN 1 ELSE 0 END)` counts a subset per group."}],"id":"bl_05_007"}]},{"id":"card_05_004","type":"card","x":592,"y":137,"w":560,"h":499,"z":15,"title":"Joins","sectionNumber":"5","accent":"accent","bg":"surface","showHeader":true,"blocks":[{"type":"table","headers":["Join","Returns"],"rows":[["INNER","Rows with a match on both sides"],["LEFT","All left rows; NULLs where no match"],["FULL OUTER","All rows from both sides"],["CROSS","Every combination (Cartesian product)"],["Anti-join","Left rows with no match (`NOT EXISTS`)"],["Semi-join","Left rows with a match (`EXISTS`)"]],"id":"bl_05_008"},{"type":"code","language":"sql","code":"SELECT c.customer_id\nFROM customers c\nLEFT JOIN orders o ON o.customer_id = c.customer_id\nWHERE o.order_id IS NULL; -- customers with no orders","id":"bl_05_009"}]},{"id":"card_05_005","type":"card","x":592,"y":660,"w":560,"h":406,"z":16,"title":"Join Cardinality","sectionNumber":"6","accent":"accent","bg":"surface","showHeader":true,"blocks":[{"type":"text","text":"Most wrong totals come from joins that silently change the grain of the data.","id":"bl_05_00a"},{"type":"keyvalue","items":[{"term":"One-to-one","desc":"Row count stays the same."},{"term":"One-to-many","desc":"Row count grows to the \"many\" side."},{"term":"Many-to-many","desc":"Can multiply rows and double-count sums."},{"term":"Check grain","desc":"Know what one row means on each side before joining."}],"id":"bl_05_00b"},{"type":"callout","variant":"warn","title":"Filtering a LEFT JOIN in WHERE","text":"A WHERE condition on the right table turns a LEFT JOIN into an INNER JOIN. Move it into the ON clause instead.","id":"bl_05_00c"}]},{"id":"card_05_006","type":"card","x":592,"y":1090,"w":560,"h":512,"z":17,"title":"CTEs and Subqueries","sectionNumber":"7","accent":"accent","bg":"surface","showHeader":true,"blocks":[{"type":"code","language":"sql","code":"WITH paid AS (\n SELECT user_id, SUM(revenue) AS revenue\n FROM orders\n WHERE status = 'paid'\n GROUP BY user_id\n)\nSELECT u.country, AVG(p.revenue) AS avg_rev\nFROM paid p\nJOIN users u USING (user_id)\nGROUP BY u.country;","id":"bl_05_00d"},{"type":"keyvalue","items":[{"term":"CTE","desc":"Names an intermediate step; makes long queries readable and testable."},{"term":"Recursive CTE","desc":"Walks hierarchies such as org charts or category trees."},{"term":"EXISTS","desc":"Stops at the first match; NOT EXISTS is NULL-safe, unlike NOT IN."}],"id":"bl_05_00e"}]},{"id":"card_05_007","type":"card","x":1184,"y":137,"w":560,"h":521,"z":18,"title":"Window Functions","sectionNumber":"8","accent":"accent","bg":"surface","showHeader":true,"blocks":[{"type":"code","language":"sql","code":"SELECT user_id, order_date, revenue,\n ROW_NUMBER() OVER w AS rn,\n SUM(revenue) OVER (\n PARTITION BY user_id ORDER BY order_date\n ROWS BETWEEN UNBOUNDED PRECEDING\n AND CURRENT ROW) AS running_total,\n LAG(revenue) OVER w AS prev_revenue\nFROM orders\nWINDOW w AS (PARTITION BY user_id\n ORDER BY order_date);","id":"bl_05_00f"},{"type":"table","headers":["Function","Ties"],"rows":[["ROW_NUMBER","Unique 1, 2, 3, 4"],["RANK","Gaps: 1, 2, 2, 4"],["DENSE_RANK","No gaps: 1, 2, 2, 3"]],"id":"bl_05_00g"}]},{"id":"card_05_008","type":"card","x":1184,"y":682,"w":560,"h":395,"z":19,"title":"Top-N per Group and Dedup","sectionNumber":"9","accent":"accent","bg":"surface","showHeader":true,"blocks":[{"type":"code","language":"sql","code":"WITH ranked AS (\n SELECT *,\n ROW_NUMBER() OVER (\n PARTITION BY user_id\n ORDER BY updated_at DESC, id DESC) AS rn\n FROM events\n)\nSELECT * FROM ranked WHERE rn = 1;","id":"bl_05_00h"},{"type":"keyvalue","items":[{"term":"Tie-breaker","desc":"Add a unique column to ORDER BY so the kept row is deterministic."},{"term":"Top 3 per group","desc":"Same pattern with `rn <= 3`."}],"id":"bl_05_00i"}]},{"id":"card_05_009","type":"card","x":1184,"y":1101,"w":560,"h":429,"z":20,"title":"Dates and Time","sectionNumber":"10","accent":"accent","bg":"surface","showHeader":true,"blocks":[{"type":"code","language":"sql","code":"WHERE event_time >= '2026-01-01'\n AND event_time < '2026-02-01' -- half-open\n\nSELECT DATE_TRUNC('month', created_at) AS month,\n COUNT(*) AS signups\nFROM users\nGROUP BY 1;","id":"bl_05_00j"},{"type":"keyvalue","items":[{"term":"Half-open ranges","desc":"`>= start AND < end` avoids missing the last day and double counting."},{"term":"Time zones","desc":"Store UTC; convert once at reporting time."},{"term":"Dialects","desc":"Date functions differ (DATE_TRUNC, DATEADD, INTERVAL); check your engine."}],"id":"bl_05_00k"}]},{"id":"card_05_00a","type":"card","x":1184,"y":1554,"w":560,"h":439,"z":21,"title":"NULL Semantics","sectionNumber":"11","accent":"accent","bg":"surface","showHeader":true,"blocks":[{"type":"keyvalue","items":[{"term":"Three-valued logic","desc":"Comparisons with NULL return UNKNOWN, which WHERE treats as false."},{"term":"Test with IS NULL","desc":"`col = NULL` is never true."},{"term":"COALESCE","desc":"Returns the first non-NULL argument, for defaults."},{"term":"NULLIF","desc":"`x / NULLIF(y, 0)` avoids divide-by-zero."},{"term":"Aggregates","desc":"SUM, AVG, and COUNT(col) ignore NULLs; AVG is over non-NULL rows only."}],"id":"bl_05_00l"},{"type":"callout","variant":"warn","title":"NOT IN with NULLs","text":"If the subquery returns any NULL, `NOT IN` returns no rows. Use `NOT EXISTS` instead.","id":"bl_05_00m"}]},{"id":"card_05_00b","type":"card","x":1776,"y":137,"w":560,"h":272,"z":22,"title":"Set Operations","sectionNumber":"12","accent":"accent","bg":"surface","showHeader":true,"blocks":[{"type":"keyvalue","items":[{"term":"UNION","desc":"Combines results and removes duplicates (costs a sort or hash)."},{"term":"UNION ALL","desc":"Keeps duplicates; faster and usually what you want."},{"term":"INTERSECT","desc":"Rows present in both results."},{"term":"EXCEPT / MINUS","desc":"Rows in the first result but not the second."}],"id":"bl_05_00n"}]},{"id":"card_05_00c","type":"card","x":1776,"y":433,"w":560,"h":484,"z":23,"title":"Analytics Patterns","sectionNumber":"13","accent":"accent","bg":"surface","showHeader":true,"blocks":[{"type":"code","language":"sql","code":"-- conversion rate per variant\nSELECT variant,\n AVG(CASE WHEN converted THEN 1.0\n ELSE 0.0 END) AS cvr\nFROM experiment_users\nGROUP BY variant;","id":"bl_05_00o"},{"type":"keyvalue","items":[{"term":"Cohorts","desc":"Assign each user a first-event month, then count activity by months since."},{"term":"Funnels","desc":"Count distinct users reaching each ordered step within a time window."},{"term":"Sessionization","desc":"`LAG(ts)` gaps above a threshold start a new session; a running SUM gives the id."},{"term":"Share of total","desc":"`metric / SUM(metric) OVER ()` after aggregating to the intended grain."}],"id":"bl_05_00p"}]},{"id":"card_05_00d","type":"card","x":1776,"y":941,"w":560,"h":459,"z":24,"title":"Performance Basics","sectionNumber":"14","accent":"accent","bg":"surface","showHeader":true,"blocks":[{"type":"keyvalue","items":[{"term":"Filter early","desc":"Reduce rows before joins and aggregations."},{"term":"Select needed columns","desc":"Avoid `SELECT *` in pipelines, especially on columnar stores."},{"term":"Indexes","desc":"Help selective filters and join keys in OLTP databases; cost writes."},{"term":"Partition pruning","desc":"Filter on the partition column so warehouses scan less data."},{"term":"Read the plan","desc":"`EXPLAIN` shows scans, join strategies, and row estimates."}],"id":"bl_05_00q"},{"type":"callout","variant":"tip","title":"Sargable predicates","text":"Keep the column bare: `ts >= '2026-01-01'` can use an index; `DATE(ts) = '2026-01-01'` usually cannot.","id":"bl_05_00r"}]},{"id":"card_05_00e","type":"card","x":1776,"y":1424,"w":560,"h":263,"z":25,"title":"Safety Checklist","sectionNumber":"15","accent":"accent","bg":"surface","showHeader":true,"blocks":[{"type":"list","ordered":true,"items":["Write down what one row means after every CTE.","Check row counts before and after each join.","Use half-open date ranges with explicit time zones.","Decide how NULLs should behave in every filter and metric.","Run `SELECT` with the same WHERE before any UPDATE or DELETE.","Wrap data changes in a transaction you can roll back."],"id":"bl_05_00s"}]}],"background":"dots","canvasColor":"canvas","themeId":"midnight","layoutGap":24,"updatedAt":"2026-10-02T09:26:38.558Z"}]]>SQL Query order, joins, aggregation, window functions, CTEs, dates, NULL semantics, analytics patterns, and query safety. 01Logical Query Order1FROM / JOIN2WHERE3GROUP BY4HAVING5SELECT + windows6DISTINCT7ORDER BY8LIMIT / OFFSETWritten order differs from evaluation orderWHERE vs HAVINGWHERE filters rows before grouping; HAVING filters groups after.AliasesSELECT aliases are not visible in WHERE, but most engines allow them in ORDER BY.Window functionsRun after WHERE/GROUP BY/HAVING, so filter their result in an outer query.02SELECT and FilteringSQLSELECT user_id, revenue * 1.18 AS revenue_gross FROM orders WHERE status = 'paid' AND country IN ('IN', 'US', 'GB') AND revenue BETWEEN 100 AND 500 AND email LIKE '%@example.com' AND (coupon IS NULL OR coupon <> 'TEST');ParenthesesAlways group mixed AND / OR conditions explicitly.BETWEENInclusive at both ends; prefer half-open ranges for timestamps.DISTINCTDeduplicates output rows; it can hide data-quality problems.03CASE ExpressionsSQLSELECT order_id, CASE WHEN revenue >= 1000 THEN 'high' WHEN revenue >= 100 THEN 'mid' ELSE 'low' END AS segment FROM orders;First match winsOrder WHEN branches from most to least specific.Missing ELSEUnmatched rows become NULL.04AggregationSQLSELECT country, COUNT(*) AS orders, COUNT(DISTINCT user_id) AS buyers, SUM(revenue) AS revenue, AVG(revenue) AS aov FROM orders GROUP BY country HAVING COUNT(*) >= 100 ORDER BY revenue DESC;COUNT(*) vs COUNT(col)COUNT(col) skips NULLs.Conditional aggregationSUM(CASE WHEN … THEN 1 ELSE 0 END) counts a subset per group.05JoinsJoinReturnsINNERRows with a match on both sidesLEFTAll left rows; NULLs where no matchFULL OUTERAll rows from both sidesCROSSEvery combination (Cartesian product)Anti-joinLeft rows with no match (NOT EXISTS)Semi-joinLeft rows with a match (EXISTS)SQLSELECT c.customer_id FROM customers c LEFT JOIN orders o ON o.customer_id = c.customer_id WHERE o.order_id IS NULL; -- customers with no orders06Join CardinalityMost wrong totals come from joins that silently change the grain of the data. One-to-oneRow count stays the same.One-to-manyRow count grows to the "many" side.Many-to-manyCan multiply rows and double-count sums.Check grainKnow what one row means on each side before joining.Filtering a LEFT JOIN in WHEREA WHERE condition on the right table turns a LEFT JOIN into an INNER JOIN. Move it into the ON clause instead. 07CTEs and SubqueriesSQLWITH paid AS ( SELECT user_id, SUM(revenue) AS revenue FROM orders WHERE status = 'paid' GROUP BY user_id ) SELECT u.country, AVG(p.revenue) AS avg_rev FROM paid p JOIN users u USING (user_id) GROUP BY u.country;CTENames an intermediate step; makes long queries readable and testable.Recursive CTEWalks hierarchies such as org charts or category trees.EXISTSStops at the first match; NOT EXISTS is NULL-safe, unlike NOT IN.08Window FunctionsSQLSELECT user_id, order_date, revenue, ROW_NUMBER() OVER w AS rn, SUM(revenue) OVER ( PARTITION BY user_id ORDER BY order_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_total, LAG(revenue) OVER w AS prev_revenue FROM orders WINDOW w AS (PARTITION BY user_id ORDER BY order_date);FunctionTiesROW_NUMBERUnique 1, 2, 3, 4RANKGaps: 1, 2, 2, 4DENSE_RANKNo gaps: 1, 2, 2, 309Top-N per Group and DedupSQLWITH ranked AS ( SELECT *, ROW_NUMBER() OVER ( PARTITION BY user_id ORDER BY updated_at DESC, id DESC) AS rn FROM events ) SELECT * FROM ranked WHERE rn = 1;Tie-breakerAdd a unique column to ORDER BY so the kept row is deterministic.Top 3 per groupSame pattern with rn <= 3.10Dates and TimeSQLWHERE event_time >= '2026-01-01' AND event_time < '2026-02-01' -- half-open SELECT DATE_TRUNC('month', created_at) AS month, COUNT(*) AS signups FROM users GROUP BY 1;Half-open ranges>= start AND < end avoids missing the last day and double counting.Time zonesStore UTC; convert once at reporting time.DialectsDate functions differ (DATE_TRUNC, DATEADD, INTERVAL); check your engine.11NULL SemanticsThree-valued logicComparisons with NULL return UNKNOWN, which WHERE treats as false.Test with IS NULLcol = NULL is never true.COALESCEReturns the first non-NULL argument, for defaults.NULLIFx / NULLIF(y, 0) avoids divide-by-zero.AggregatesSUM, AVG, and COUNT(col) ignore NULLs; AVG is over non-NULL rows only.NOT IN with NULLsIf the subquery returns any NULL, NOT IN returns no rows. Use NOT EXISTS instead. 12Set OperationsUNIONCombines results and removes duplicates (costs a sort or hash).UNION ALLKeeps duplicates; faster and usually what you want.INTERSECTRows present in both results.EXCEPT / MINUSRows in the first result but not the second.13Analytics PatternsSQL-- conversion rate per variant SELECT variant, AVG(CASE WHEN converted THEN 1.0 ELSE 0.0 END) AS cvr FROM experiment_users GROUP BY variant;CohortsAssign each user a first-event month, then count activity by months since.FunnelsCount distinct users reaching each ordered step within a time window.SessionizationLAG(ts) gaps above a threshold start a new session; a running SUM gives the id.Share of totalmetric / SUM(metric) OVER () after aggregating to the intended grain.14Performance BasicsFilter earlyReduce rows before joins and aggregations.Select needed columnsAvoid SELECT * in pipelines, especially on columnar stores.IndexesHelp selective filters and join keys in OLTP databases; cost writes.Partition pruningFilter on the partition column so warehouses scan less data.Read the planEXPLAIN shows scans, join strategies, and row estimates.Sargable predicatesKeep the column bare: ts >= '2026-01-01' can use an index; DATE(ts) = '2026-01-01' usually cannot. 15Safety Checklist1.Write down what one row means after every CTE.2.Check row counts before and after each join.3.Use half-open date ranges with explicit time zones.4.Decide how NULLs should behave in every filter and metric.5.Run SELECT with the same WHERE before any UPDATE or DELETE.6.Wrap data changes in a transaction you can roll back.