A year-to-date total needs a benchmark, and the fair one is last year up to the same date: 1 January to 15 May against 1 January to 15 May. Move the reference date back one year and apply the same period-to-date range to both years.
A small example
Two years of orders. The 2026 rows are the same as in how to calculate YTD, QTD and MTD, so this year’s total is again 500.
CREATE TEMP TABLE Sales (OrderDate DATE, Amount NUMBER(10,2));
INSERT INTO Sales VALUES
('2025-01-12', 90), ('2025-03-03', 110), ('2025-05-10', 60),
('2025-05-15', 40), ('2025-05-16', 70), ('2025-11-20', 300),
('2026-01-10', 100), ('2026-03-05', 150), ('2026-04-08', 80),
('2026-05-02', 120), ('2026-05-15', 50), ('2026-05-20', 300);
One row per period
WITH Params AS (
SELECT '2026-05-15'::DATE AS AsOf -- CURRENT_DATE() in a live report
),
Periods AS (
SELECT 'This year' AS Period,
DATE_TRUNC('year', AsOf) AS StartDate,
AsOf AS EndDate
FROM Params
UNION ALL
SELECT 'Last year',
DATE_TRUNC('year', DATEADD(year, -1, AsOf)),
DATEADD(year, -1, AsOf)
FROM Params
)
SELECT p.Period, p.StartDate, p.EndDate,
COALESCE(SUM(s.Amount), 0) AS Sales
FROM Periods p
LEFT JOIN Sales s
ON s.OrderDate >= p.StartDate
AND s.OrderDate < DATEADD(day, 1, p.EndDate)
GROUP BY p.Period, p.StartDate, p.EndDate
ORDER BY p.StartDate DESC;
| Period | StartDate | EndDate | Sales |
|---|---|---|---|
| This year | 2026-01-01 | 2026-05-15 | 500.00 |
| Last year | 2025-01-01 | 2025-05-15 | 300.00 |
Showing the boundaries next to the totals makes the result easy to check: anyone reading it can
see that both periods end on 15 May. The order of 16 May 2025 is left out by one day, as it should
be, and so is the one of 20 May 2026. The LEFT JOIN keeps a period with no orders, and
COALESCE turns its total into 0.
Side by side
A dashboard tile usually wants a single row. Keep the same two CTEs and replace the final SELECT:
SELECT
SUM(IFF(p.Period = 'This year', s.Amount, 0)) AS YTD,
SUM(IFF(p.Period = 'Last year', s.Amount, 0)) AS YTDLastYear
FROM Periods p
LEFT JOIN Sales s
ON s.OrderDate >= p.StartDate
AND s.OrderDate < DATEADD(day, 1, p.EndDate);
| YTD | YTDLastYear |
|---|---|
| 500.00 | 300.00 |
This year is 200 ahead of last year, a 66.7% increase. How to calculate YTD growth % vs prior year covers the percentage, including products that sold nothing last year.
Not against the whole of last year
The full year 2025 adds up to 670. Comparing this year’s 500 with it suggests that sales fell by 25.4%, while like for like they grew by 66.7%. The distortion shrinks as the year goes on, so it is obvious in May and easy to miss in November. The same mistake happens within a month; see how to compare an incomplete month with the same days of last month.
Edge cases
- Data loaded overnight. If today’s rows are not in the table yet, compare as of yesterday. Otherwise an empty or partial day this year is compared with a complete day last year.
- 29 February.
DATEADD(year, -1, '2024-02-29'::DATE)returns 2023-02-28: Snowflake moves the day back to the end of the shorter month. The other way round, as of 28 February 2025 last year ends on 28 February 2024, and 29 February 2024 is not counted. Both readings are defensible; choose one and say which in the report. See how to handle 29 February when comparing with last year. - Weekdays. 15 May 2026 is a Friday, while 15 May 2025 was a Thursday. When the weekly pattern matters more than the calendar date, as in retail, compare with the date 364 days earlier: it always falls on the same weekday. See how to compare with the same weekday last year.