On 15 May, May’s sales cover half a month. Compared with the whole of April they always look like a drop. The fair benchmark is the same days of April: 1 to 15 May against 1 to 15 April.

A small example

CREATE TEMP TABLE Sales (OrderDate DATE, Amount NUMBER(10,2));
INSERT INTO Sales VALUES
  ('2026-04-03', 60),  ('2026-04-08', 80),  ('2026-04-15', 40),
  ('2026-04-16', 90),  ('2026-04-28', 200),
  ('2026-05-02', 120), ('2026-05-15', 50),  ('2026-05-20', 300);

The query

The structure is the same as in how to compare YTD with the same period last year, with months in place of years.

WITH Params AS (
  SELECT '2026-05-15'::DATE AS AsOf   -- CURRENT_DATE() in a live report
),
Periods AS (
  SELECT 'This month' AS Period,
         DATE_TRUNC('month', AsOf) AS StartDate,
         AsOf AS EndDate
  FROM Params
  UNION ALL
  SELECT 'Last month',
         DATE_TRUNC('month', DATEADD(month, -1, AsOf)),
         DATEADD(month, -1, AsOf)
  FROM Params
)
SELECT p.Period, p.StartDate, p.EndDate,
       DATEDIFF(day, p.StartDate, p.EndDate) + 1 AS DayCount,
       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 DayCount Sales
This month 2026-05-01 2026-05-15 15 170.00
Last month 2026-04-01 2026-04-15 15 180.00

Over the same fifteen days, May is 5.6% behind April. Against the whole of April, 470, it would look 63.8% behind.

When the days do not line up

Months have 28 to 31 days, so near the end of a month the same day number may not exist in the previous month. DATEADD then moves to the last day of the shorter month:

AsOf DATEADD(month, -1, AsOf) Month-end rule
2026-03-28 2026-02-28 2026-02-28
2026-03-30 2026-02-28 2026-02-28
2026-03-31 2026-02-28 2026-02-28
2026-04-30 2026-03-30 2026-03-31
2026-05-31 2026-04-30 2026-04-30

From 28 March on, the benchmark is the whole of February, while March keeps growing to 29, 30 and 31 days. That cannot be avoided, and the DayCount column makes it visible.

The opposite case is a real error. On 30 April the month is complete, but DATEADD returns 30 March, so all of April is compared with March without its last day. At month end, compare with the end of the previous month instead. In the Periods CTE, replace the EndDate of last month, DATEADD(month, -1, AsOf), with:

IFF(AsOf = LAST_DAY(AsOf),
    LAST_DAY(DATEADD(month, -1, AsOf)),
    DATEADD(month, -1, AsOf))

That is the third column of the table above.

Sales per day

When the two periods have different lengths, compare the average per day rather than the total. Add this column to the final SELECT:

COALESCE(SUM(s.Amount), 0) / (DATEDIFF(day, p.StartDate, p.EndDate) + 1) AS SalesPerDay

On 31 March, 31 days of March against 28 days of February is then a fair comparison. The mix of weekdays still differs: a period with one more weekend can move the average on its own.

Early in the month

On 1 or 2 May the comparison covers one or two days, and a single large order changes the percentage completely. Many reports show the month-to-date comparison only from the second week, or show the amounts without a percentage until then.

If the table is loaded overnight, use yesterday as the reference date, as explained in how to calculate YTD, QTD and MTD. Otherwise an empty day this month is compared with a complete day last month.

References