SQL Benchmarking — Applied LAG/LEAD, RANK, Scoring

ADVCompetitive AnalysisLAG / LEADRANK / DENSE_RANKCASE WHEN PivotPERCENT_RANKPostgreSQL Compatible5 questions
1 / 5 · In progress 0 / 5 completed
QUESTION 6

LAG() + CTE — Visualize Quarterly Competitor Trend Acceleration and Slowdown with Period-over-Period Growth

LAG / LEADCTEPeriod-over-Period GrowthNULLIF · Zero-Division Protection
Background

LAG(expr, offset, default) is a window function that returns the value offset rows before the current row. It is an essential pattern for period-over-period and year-over-year calculations.

LAG(revenue) OVER (
  PARTITION BY company_name   -- Do not take the preceding row across companies
  ORDER BY     quarter        -- Define the “one row earlier” position by quarter
)                              -- Default offset=1, default=NULL
Prevent division by zero with NULLIF: if the previous period is 0, revenue / prev_revenue causes a division-by-zero error. NULLIF(prev_revenue, 0) returns NULL when the value is 0, so division by NULL produces NULL (not an error) and is handled safely.
Calculate LAG only once in a CTE: writing LAG directly multiple times in the SELECT for a growth-rate calculation reduces maintainability. First establish prev_revenue in a CTE and reuse it in the outer query as a best practice.
Problem

Using quarterly revenue from three companies, calculate the previous-period revenue (prev_revenue) and period-over-period growth rate (growth_rate_pct, rounded to one decimal place). Return company_name, quarter, revenue, prev_revenue, growth_rate_pct, sorted by company_name ascending and then quarter ascending. Q1 has NULL for both prev_revenue and growth_rate_pct.

Tables
▸ quarterly_revenue
company_namequarterrevenue
AlphaTech2023Q1300
AlphaTech2023Q2360
AlphaTech2023Q3280
AlphaTech2023Q4400
BetaSoft2023Q1250
BetaSoft2023Q2220
BetaSoft2023Q3290
BetaSoft2023Q4310
GammaSys2023Q1120
GammaSys2023Q2150
GammaSys2023Q3170
GammaSys2023Q4160

※ Revenue unit: JPY 100 million

Expected Output
company_namequarterrevenueprev_revenuegrowth_rate_pct
AlphaTech2023Q1300NULLNULL
AlphaTech2023Q236030020.0
AlphaTech2023Q3280360-22.2
AlphaTech2023Q440028042.9
BetaSoft2023Q1250NULLNULL
BetaSoft2023Q2220250-12.0
BetaSoft2023Q329022031.8
BetaSoft2023Q43102906.9
GammaSys2023Q1120NULLNULL
GammaSys2023Q215012025.0
GammaSys2023Q317015013.3
GammaSys2023Q4160170-5.9
QUESTION 7

RANK() + CTE — Category-Level Competitor Ranking and Top-N Filtering

RANK / DENSE_RANKCTE + WHERECategory Top-NHandling Tied Ranks
Background

RANK(), DENSE_RANK(), and ROW_NUMBER() are all window functions that return ranks, but they handle ties differently.

FunctionWhen two rows tie for secondNext rank
ROW_NUMBER()2, 3 (pseudo-unique assignment)4
RANK()2, 2 (tie)4 (skips a number)
DENSE_RANK()2, 2 (tie)3 (consecutive)
WITH ranked AS (
  SELECT *, RANK() OVER (PARTITION BY category ORDER BY annual_revenue DESC) AS rnk
  FROM product_revenue
)
SELECT * FROM ranked WHERE rnk <= 2;  -- Top rank 2 or better in each category, including ties
The CTE + WHERE top-N extraction pattern: a window-function result cannot be referenced in WHERE in the same SELECT (WHERE comes earlier in the logical evaluation order). Assign the rank in a CTE and filter it in the outer WHERE as the standard pattern.
Problem

From the competitor revenue data for each category, assign the within-category sales rank (rank_in_category) and return only companies ranked 2 or better in each category, including ties. Return category, company_name, annual_revenue, rank_in_category, with categories in the specified AI/ML, Cloud, Security, Data Analytics order, rank_in_category ascending within each category, and company_name ascending for ties.

Tables
▸ product_revenue
company_namecategoryannual_revenue
AlphaTechCloud4200
AlphaTechSecurity1800
AlphaTechAI/ML950
BetaSoftCloud2800
BetaSoftAI/ML1500
BetaSoftData Analytics800
GammaSysSecurity900
GammaSysAI/ML650
GammaSysData Analytics420
DeltaNetCloud1200
DeltaNetData Analytics600

※ annual_revenue unit: JPY 100 million

Expected Output
categorycompany_nameannual_revenuerank_in_category
AI/MLBetaSoft15001
AI/MLAlphaTech9502
CloudAlphaTech42001
CloudBetaSoft28002
SecurityAlphaTech18001
SecurityGammaSys9002
Data AnalyticsBetaSoft8001
Data AnalyticsDeltaNet6002
QUESTION 8

Multiple-CTE Scoring — Automatically Calculate Overall KPI Grades with Staged Aggregation

Multiple WITH CTEsCASE WHENCompetitor ScoringStaged Aggregation and Grading
Background

Multiple CTEs can be defined as comma-separated entries in a WITH clause, and a preceding CTE can be referenced by a later CTE. This decomposes complex aggregation into named, readable stages.

WITH
step1 AS (
  SELECT ..., CASE WHEN col >= 30 THEN 3 ELSE 1 END AS score
  FROM source_table
),
step2 AS (          -- step1 can be referenced here
  SELECT *, score_a + score_b AS total
  FROM step1
)
SELECT * FROM step2;
Be careful when omitting ELSE from CASE WHEN: omitting ELSE in CASE WHEN ... END means ELSE NULL. In scoring logic, always specify ELSE the minimum score.
Problem

From KPI data for six competitors, score revenue scale, growth rate, and NPS from 1 to 3 points, then calculate the total (maximum 9 points) and overall grade (S/A/B/C). Return company_name, revenue_score, growth_score, nps_pts, total_score, grade, sorted by total_score descending and then company_name ascending.

Scoring rules: revenue_score (revenue_bn ≥30→3, ≥10→2, else 1) | growth_score (growth_pct ≥25→3, ≥10→2, else 1) | nps_pts (nps_score ≥65→3, ≥50→2, else 1) | grade (total ≥8→S, ≥6→A, ≥4→B, else C)

Tables
▸ competitor_kpis
company_namerevenue_bngrowth_pctnps_score
AlphaTech42.018.572
BetaSoft28.012.358
GammaSys15.031.265
DeltaNet5.08.148
EpsilonSys3.0-2.441
ZetaCloud2.05.655

※ revenue_bn: revenue (billions of JPY) / growth_pct: year-over-year growth (%) / nps_score: customer NPS

Expected Output
company_namerevenue_scoregrowth_scorenps_ptstotal_scoregrade
AlphaTech3238S
GammaSys2338S
BetaSoft2226A
ZetaCloud1124B
DeltaNet1113C
EpsilonSys1113C
QUESTION 9

CASE WHEN Pivot — Convert Quarterly Revenue from Long to Wide and Calculate Within-Year Growth

SUM + CASE WHENGROUP BYLong-to-Wide TransformationConditional Aggregation and NULLIF
Background

Conditional aggregation uses the SUM(CASE WHEN ... END) pattern to convert long-format (row-oriented) data into wide-format (column-oriented) data. PostgreSQL has no dedicated PIVOT syntax, so this is the standard implementation.

SELECT
  company_name,
  SUM(CASE WHEN quarter = 'Q1' THEN revenue ELSE 0 END) AS q1_rev,
  SUM(CASE WHEN quarter = 'Q2' THEN revenue ELSE 0 END) AS q2_rev
FROM   quarterly_sales
GROUP BY company_name;
ELSE 0 vs ELSE NULL: ELSE 0 adds 0 for non-matching rows. With ELSE NULL, SUM ignores NULL, so the result is the same, but AVG and COUNT behave differently. ELSE 0 is the conventional choice for pivot aggregation.
Problem

Using quarterly revenue for three companies in 2024, pivot Q1–Q4 revenue into columns, then calculate the annual total (annual_total) and the within-year Q1→Q4 growth rate (q4_vs_q1_pct, rounded to one decimal place). Return company_name, q1_rev, q2_rev, q3_rev, q4_rev, annual_total, q4_vs_q1_pct, sorted by annual_total descending.

Tables
▸ quarterly_sales
company_nameyearquarterrevenue
AlphaTech2024Q1340
AlphaTech2024Q2410
AlphaTech2024Q3320
AlphaTech2024Q4450
BetaSoft2024Q1260
BetaSoft2024Q2240
BetaSoft2024Q3310
BetaSoft2024Q4340
GammaSys2024Q1130
GammaSys2024Q2165
GammaSys2024Q3185
GammaSys2024Q4170

※ Revenue unit: JPY 100 million / the table is assumed to contain multiple years, so filter with WHERE year = 2024

Expected Output
company_nameq1_revq2_revq3_revq4_revannual_totalq4_vs_q1_pct
AlphaTech340410320450152032.4
BetaSoft260240310340115030.8
GammaSys13016518517065030.8
QUESTION 10

PERCENT_RANK() + LAG() + Two-Stage CTE — Track Annual Changes in Competitor Relative Position

PERCENT_RANKLAG + Two-Stage CTERelative-Position ChangePercentile Tracking
Background

PERCENT_RANK() returns each row’s relative rank as a percentile from 0.0 to 1.0. The formula is (rank - 1) / (total_rows - 1).

FormulaExample with 5 rows (ORDER BY revenue ASC)
(rank-1) / (5-1)Minimum revenue=0.0 / middle=0.5 / maximum revenue=1.0
PERCENT_RANK() OVER (
  PARTITION BY year             -- Independent rank space for each year
  ORDER BY     revenue_bn       -- Ascending: smallest→0.0, largest→1.0
) * 100                         -- Convert to a percentage (0–100)
Dependent processing with two CTE stages: “calculate PERCENT_RANK” → “retrieve the previous year’s PERCENT_RANK with LAG” has a dependency, so the first CTE establishes pct_rank and the second CTE applies LAG.
Problem

From two years of revenue data for five companies, calculate relative revenue position within each year (pct_rank, percentage rounded to one decimal place) and the year-over-year position change (pct_rank_change). Return company_name, year, revenue_bn, pct_rank, prev_pct_rank, pct_rank_change, sorted by year ascending and then pct_rank descending.

Tables
▸ annual_performance
company_nameyearrevenue_bn
AlphaTech202235.0
BetaSoft202226.0
GammaSys202218.0
DeltaNet202212.0
EpsilonSys20227.0
AlphaTech202342.0
BetaSoft202328.0
GammaSys202315.0
DeltaNet202316.0
EpsilonSys20239.0
Expected Output
company_nameyearrevenue_bnpct_rankprev_pct_rankpct_rank_change
AlphaTech202235.0100.0NULLNULL
BetaSoft202226.075.0NULLNULL
GammaSys202218.050.0NULLNULL
DeltaNet202212.025.0NULLNULL
EpsilonSys20227.00.0NULLNULL
AlphaTech202342.0100.0100.00.0
BetaSoft202328.075.075.00.0
DeltaNet202316.050.025.025.0
GammaSys202315.025.050.0-25.0
EpsilonSys20239.00.00.00.0