Windows II — frames & running
Concept  ·  SQL & Querying

The first N-1 rows of a moving average are partial windows — declare or exclude them.

The frame clause defines an intent of N rows, but at the start of a partition there aren't N rows yet, and AVG quietly averages whatever the frame contains. Nothing is wrong mechanically — the wrongness is in the chart legend that says '3-day average' over points that are 1-day averages. Three honest treatments: exclude the warm-up rows, flag them (a partial boolean via COUNT(*) OVER the same frame < N), or accept and say so.

See it break
visits
dn
110
220
330
440
The trap
SELECT
  d,
  AVG(n) OVER (ORDER BY
      d ROWS BETWEEN 2 PRECEDING
      AND CURRENT ROW
  ) AS avg3
FROM
  visits
ORDER BY
  d;
The fix
SELECT
  d,
  AVG(n) OVER w AS avg3,
  (COUNT(*) OVER w < 3) AS partial
FROM
  visits
WINDOW
  w AS (
    ORDER BY
      d ROWS BETWEEN 2 PRECEDING
      AND CURRENT ROW
  )
ORDER BY
  d;
The trap
110
215
320
430
The fix
110true
215true
320false
430false

The first rows of the 3-day average ran over partial windows — day 1 averaged one point, day 2 two — but the chart called every point a '3-day average'.

The rule

Exclude the warm-up rows, flag them with a partial column, or disclose in the label — the deliverable is the disclosure, not a different number.

Shows up elsewhere
ROWS counts rows, RANGE counts timeDefault frame includes peer ties
Try one →Prove it in practice →