create table tx
(
i serial not null primary key,
n varchar(10), d date,
constraint ux_tx unique(n,d)
);
insert into tx(n,d) values
('john','2012-7-3'),
('john','2012-7-5'),
('john','2012-7-6'),
('john','2012-7-9'),
('john','2012-7-12'),
('john','2012-7-13'),
('john','2012-7-16'),
('john','2012-7-17'),
('john','2012-7-18'),
('john','2012-7-20'),
('john','2012-7-30'),
('john','2012-7-31'),
('paul','2012-7-3'),
('paul','2012-7-5'),
('paul','2012-7-18'),
('paul','2012-7-19'),
('paul','2012-7-20'),
('paul','2012-7-23'),
('paul','2012-7-24'),
('paul','2012-7-25'),
('paul','2012-7-26'),
('paul','2012-7-27'),
('paul','2012-7-30'),
('paul','2012-7-31'),
('paul','2012-8-1'),
('paul','2012-8-3'),
('paul','2012-8-6'),
('paul','2012-8-7');
create table holiday(d date);
insert into holiday(d) values
('2012-7-4');
-- Monday logic sourced here: http://www.ienablemuch.com/2010/12/finding-previous-day-of-week.html
with first_date as
(
-- select dateadd( ww, datediff(ww,0,min(d)), 0 ) as first_date -- get the monday of the earliest date
select previous_date_of_day(min(d), 1) as first_date
from tx
)
,shifted as
(
select
tx.n, tx.d,
(tx.d - fd.first_date) - ( (tx.d - fd.first_date) / 7 * 2 ) as diff
from tx
cross join first_date fd
union
select
xxx.n, h.d,
(h.d - fd.first_date) - ((h.d - fd.first_date) / 7 * 2) as diff
from holiday h
cross join first_date fd
cross join (select distinct n from tx) as xxx
)
,grouped as
(
select
*
, diff - row_number() over(partition by n order by d) as grp
from shifted
)
select
-- remove staging columns from the output...
-- *
-- ...just output what the user will see:
d, n
,dense_rank() over (partition by n order by grp) as nth_streak
,count(*) over (partition by n, grp) as streak
from grouped
where d not in (select d from holiday) -- remove the holidays
Get the previous day of the date if the day is not exactly on that date:
create or replace function previous_date_of_day(the_date date, dow int) returns date
as
$$
select
case when extract(dow from $1) < $2 then
$1 - ( extract(dow from $1) + (7 - $2) )::int
else
$1 - ( extract(dow from $1) - $2)::int
end;
$$ language 'sql';
No comments:
Post a Comment