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