Window function
Instead of calling COALESCE you can directly specify the window function lag() . A small detail in this case, since all columns are NOT NULL defined. But it can be important to distinguish the "previous line" from the "NULL in the previous line".
SELECT id, val FROM ( SELECT id, val, lag(val, 1, val) OVER (ORDER BY id) <> val AS changed FROM p ) sub WHERE changed ORDER BY id;
Calculate the result of the comparison immediately, as the previous value is not of interest as such, only a possible change. Shorter and maybe a little faster.
If you think the first row should be “modified” (as opposed to your demo output), you need to respect NULL values, even if your columns are NOT NULL defined. Basic lag() returns NULL if the previous line is missing:
SELECT id, val FROM ( SELECT id, val, lag(val) OVER (ORDER BY id) IS DISTINCT FROM val AS changed FROM p ) sub WHERE changed ORDER BY id;
Or again, use the additional lag() options:
SELECT id, val FROM ( SELECT id, val, lag(val, 1, NOT val) OVER (ORDER BY id) <> val AS changed FROM p ) sub WHERE changed ORDER BY id;
Recursive CTE
As a proof of concept. :) Performance will not keep up with published alternatives.
WITH RECURSIVE cte AS ( SELECT id, val FROM p WHERE NOT EXISTS ( SELECT 1 FROM p p0 WHERE p0.id < p.id ) UNION ALL SELECT p.id, p.val FROM cte JOIN p ON p.id > cte.id AND p.val <> cte.val WHERE NOT EXISTS ( SELECT 1 FROM p p0 WHERE p0.id > cte.id AND p0.val <> cte.val AND p0.id < p.id ) ) SELECT * FROM cte;
With an improvement from @wildplasser.
SQL Fiddle demonstrating everything.