Recipe Tableau and SQL
How do I make a running total that restarts every year?
Total each month, then add the months up in date order: RUNNING_SUM(SUM([revenue])) in Tableau, SUM(revenue) OVER (PARTITION BY year ORDER BY month ROWS UNBOUNDED PRECEDING) in SQL. The partition is where it restarts. At this kayak shop, 2026 beat 2025 in 8 of the first 9 months, yet the year-to-date gap through month 9 is −$2,047: one stormy July outweighed the rest.
The data
Tidewater Kayak Co. bookings, January 2025 to September 2026
A seasonal kayak rental on a tidal estuary. 2026 started stronger than 2025 every month, then a storm closed the water for two weeks of July.
booking_id | booking_date | trip | party_size | revenue |
|---|---|---|---|---|
| B-20001 | 2025-01-01 | Half-day estuary tour | 3 | 256.5 |
| B-20002 | 2025-01-03 | Sunset paddle tour | 3 | 175.5 |
| B-20003 | 2025-01-05 | Sunset paddle tour | 2 | 130 |
| B-20004 | 2025-01-05 | Sunset paddle tour | 2 | 130 |
Step 1
Total each month
The file has one row per booking, often several on the same day. Totalling to one row per month first gives the running total one clean step per month. The shape is the season: quiet winters, a summer peak, and a dent in July 2026.
Tableau
SUM([revenue])
// Columns: MONTH([booking_date]) as a continuous monthSQL
SELECT
CAST (date_trunc('month', booking_date) AS DATE) AS month,
SUM(revenue) AS revenue
FROM input
GROUP BY 1
ORDER BY 1Rows
| Month | Revenue |
|---|---|
| 2025-01-01 | $2,055 |
| 2025-02-01 | $1,826 |
| 2025-03-01 | $3,656 |
| 2025-04-01 | $5,761 |
| 2025-05-01 | $8,782 |
| 2025-06-01 | $14,033 |
| 2025-07-01 | $19,164 |
| 2025-08-01 | $16,806 |
| 2025-09-01 | $9,931 |
| 2025-10-01 | $5,461 |
| 2025-11-01 | $2,440 |
| 2025-12-01 | $2,081 |
| 2026-01-01 | $2,430 |
| 2026-02-01 | $2,024 |
| 2026-03-01 | $3,768 |
| 2026-04-01 | $5,896 |
| 2026-05-01 | $9,235 |
| 2026-06-01 | $16,540 |
| 2026-07-01 | $10,076 |
| 2026-08-01 | $17,799 |
| 2026-09-01 | $12,198 |
Step 2
Add the months up, never restarting
Each point is everything booked so far. The line is steep in summer and flat in winter. Useful for lifetime totals, but it can't tell you whether this year is ahead, because 2026 starts on top of all of 2025.
Tableau
RUNNING_SUM(SUM([revenue]))
// Compute Using: Table (across)SQL
WITH m AS (
SELECT
CAST (date_trunc('month', booking_date) AS DATE) AS month,
SUM(revenue) AS revenue
FROM input
GROUP BY 1
)
SELECT
month,
revenue,
SUM(revenue) OVER (ORDER BY month ROWS UNBOUNDED PRECEDING) AS running_total
FROM m
ORDER BY monthRows
| Month | Revenue | Running total |
|---|---|---|
| 2025-01-01 | $2,055 | $2,055 |
| 2025-02-01 | $1,826 | $3,880 |
| 2025-03-01 | $3,656 | $7,537 |
| 2025-04-01 | $5,761 | $13,298 |
| 2025-05-01 | $8,782 | $22,079 |
| 2025-06-01 | $14,033 | $36,112 |
| 2025-07-01 | $19,164 | $55,277 |
| 2025-08-01 | $16,806 | $72,083 |
| 2025-09-01 | $9,931 | $82,014 |
| 2025-10-01 | $5,461 | $87,475 |
| 2025-11-01 | $2,440 | $89,915 |
| 2025-12-01 | $2,081 | $91,996 |
| 2026-01-01 | $2,430 | $94,426 |
| 2026-02-01 | $2,024 | $96,450 |
| 2026-03-01 | $3,768 | $100,218 |
| 2026-04-01 | $5,896 | $106,115 |
| 2026-05-01 | $9,235 | $115,350 |
| 2026-06-01 | $16,540 | $131,890 |
| 2026-07-01 | $10,076 | $141,966 |
| 2026-08-01 | $17,799 | $159,765 |
| 2026-09-01 | $12,198 | $171,963 |
Step 3
Restart the total every {knob.restart}
The formula didn't change. Only the partition did: the sum now drops back to zero at the start of each {knob.restart}. In SQL that is PARTITION BY; in Tableau it is the restart-every setting in Edit Table Calculation. Restart every year and you have year to date.
Tableau
RUNNING_SUM(SUM([revenue]))
// Compute Using: Specific Dimensions, tick Month of booking_date; restart every: {knob.restart} of booking_dateSQL
WITH m AS (
SELECT
CAST (date_trunc('month', booking_date) AS DATE) AS month,
SUM(revenue) AS revenue
FROM input
GROUP BY 1
)
SELECT
month,
revenue,
SUM(revenue) OVER (PARTITION BY date_trunc('{knob.restart}', month) ORDER BY month ROWS UNBOUNDED PRECEDING) AS running_total
FROM m
ORDER BY monthRows
| Month | Revenue | Running total |
|---|---|---|
| 2025-01-01 | $2,055 | $2,055 |
| 2025-02-01 | $1,826 | $3,880 |
| 2025-03-01 | $3,656 | $7,537 |
| 2025-04-01 | $5,761 | $13,298 |
| 2025-05-01 | $8,782 | $22,079 |
| 2025-06-01 | $14,033 | $36,112 |
| 2025-07-01 | $19,164 | $55,277 |
| 2025-08-01 | $16,806 | $72,083 |
| 2025-09-01 | $9,931 | $82,014 |
| 2025-10-01 | $5,461 | $87,475 |
| 2025-11-01 | $2,440 | $89,915 |
| 2025-12-01 | $2,081 | $91,996 |
| 2026-01-01 | $2,430 | $2,430 |
| 2026-02-01 | $2,024 | $4,454 |
| 2026-03-01 | $3,768 | $8,222 |
| 2026-04-01 | $5,896 | $14,119 |
| 2026-05-01 | $9,235 | $23,354 |
| 2026-06-01 | $16,540 | $39,894 |
| 2026-07-01 | $10,076 | $49,970 |
| 2026-08-01 | $17,799 | $67,769 |
| 2026-09-01 | $12,198 | $79,967 |
Where does a running total restart?
Wherever you partition it. The sum is the same in every step above; what changes is the dimension it starts over on. In Tableau's Edit Table Calculation dialog, tick the month (the direction the sum moves) and pick the year as the restart point. In SQL the same split is ORDER BY (the direction) and PARTITION BY (the restart).
Step 4
Lay this year over last year
With month number on the axis and year on colour, both years start at zero in January. The 2026 line runs just above 2025 all spring, then July flattens it and it never catches up.
Tableau
RUNNING_SUM(SUM([revenue]))
// Columns: MONTH([booking_date]) discrete; Color: YEAR([booking_date]); Compute Using: Month of booking_dateSQL
WITH m AS (
SELECT
CAST (date_trunc('month', booking_date) AS DATE) AS month,
SUM(revenue) AS revenue
FROM input
GROUP BY 1
)
SELECT
CAST (year(month) AS VARCHAR) AS year,
month(month) : : INTEGER AS month_num,
SUM(revenue) OVER (PARTITION BY year(month) ORDER BY month ROWS UNBOUNDED PRECEDING) AS year_to_date
FROM m
ORDER BY year, month_numRows
| Year | Month num | Year to date |
|---|---|---|
| 2025 | 1 | $2,055 |
| 2025 | 2 | $3,880 |
| 2025 | 3 | $7,537 |
| 2025 | 4 | $13,298 |
| 2025 | 5 | $22,079 |
| 2025 | 6 | $36,112 |
| 2025 | 7 | $55,277 |
| 2025 | 8 | $72,083 |
| 2025 | 9 | $82,014 |
| 2025 | 10 | $87,475 |
| 2025 | 11 | $89,915 |
| 2025 | 12 | $91,996 |
| 2026 | 1 | $2,430 |
| 2026 | 2 | $4,454 |
| 2026 | 3 | $8,222 |
| 2026 | 4 | $14,119 |
| 2026 | 5 | $23,354 |
| 2026 | 6 | $39,894 |
| 2026 | 7 | $49,970 |
| 2026 | 8 | $67,769 |
| 2026 | 9 | $79,967 |
Step 5
Ahead or behind, month by month
The gap is 2026 year to date minus 2025 year to date. Six small wins built a lead; one bad month in peak season wiped it out, and two more good months didn't win it back. Monthly bars said "mostly better". The running total says "behind".
Tableau
RUNNING_SUM(SUM(IF YEAR([booking_date]) = 2026 THEN [revenue] END)) - RUNNING_SUM(SUM(IF YEAR([booking_date]) = 2025 THEN [revenue] END))
// Columns: MONTH([booking_date]) discrete; Compute Using: Month of booking_date; filter MONTH <= {knob.through}SQL
WITH m AS (
SELECT
CAST (date_trunc('month', booking_date) AS DATE) AS month,
SUM(revenue) AS revenue
FROM input
GROUP BY 1
), y AS (
SELECT
year(month) AS year,
month(month) AS month_num,
SUM(revenue) OVER (PARTITION BY year(month) ORDER BY month ROWS UNBOUNDED PRECEDING) AS ytd
FROM m
)
SELECT
month_num : : INTEGER AS month_num,
MAX(ytd) FILTER (WHERE year = 2026)
- MAX(ytd) FILTER (WHERE year = 2025) AS gap
FROM y
WHERE month_num <= 9
GROUP BY month_num
ORDER BY month_numRows
| Month num | Gap |
|---|---|
| 1 | $376 |
| 2 | $574 |
| 3 | $686 |
| 4 | $821 |
| 5 | $1,274 |
| 6 | $3,782 |
| 7 | −$5,307 |
| 8 | −$4,314 |
| 9 | −$2,047 |