OK. I have solved this in MS SQL but it should be transferable to PostgresQL. I have SQLFiddled the answer:
CREATE TABLE WidgetMonths (Month tinyint, Widget varchar(1), Value int)
CREATE TABLE Months (Month tinyint, MonthOrder tinyint)
insert into WidgetMonths Values
(12, 'A', 3),
(1,'B', 5),
(2,'B', 6),
(3,'B', 7);
insert into Months Values
(12, 1), (1, 2), (2, 3), (3, 4)
Select
AllWidgetMonths.Widget,
AllWidgetMonths.Month,
IsNull(wm.Value,0) - IsNull(wmn.Value,0) as Value
from (
select Distinct Widget, Months.Month, Months.MonthOrder
from WidgetMonths
Cross Join months
) AllWidgetMonths
left join WidgetMonths wm on wm.Widget = AllWidgetMonths.Widget
AND wm.Month = AllWidgetMonths.Month
left join WidgetMonths wmn on wmn.Widget = AllWidgetMonths.Widget
AND Case When wmn.Month = 12 Then 1 Else wmn.Month + 1 End = AllWidgetMonths.Month
Order by AllWidgetMonths.Widget, AllWidgetMonths.MonthOrder
I have started off with a Table of WidgetMonths from your example the only difference being I have converted the months into a representative integer.
I have then Created the Months Table of All months we are interested in from your example. If you want months for the whole year you can simply add to this table or find another way of generating a 1-12 row result set. The MonthOrder is optional and just helped me achieve your answer ordering.
As you mentioned AllwidgetMonths has the Cross join which gives us all combinations of Widgets and Months. This maybe better achieved by a Cross join between 'Widgets' Table and the Months Table. But I wasn't sure if this existed so left this out.
We left join WidgetMonths onto our master table of All widget months to show us which months we have a value for.
The trick up the sleeve is then left joining the same table again but this time adding 1 to the month number inside the join. This shifts the rows down one. Notice I have a Case statement (not sure about this in PostgresSql) to deal with the roll over of Month 12 to Month 1. This effectively gives me the values for each month and its previous on each row of AllwidgetMonths.
The final bit is to take one value from the other.
Hey presto. I can try to update this to PostgresSQL but you may have more knowledge and can solve it quicker than I.