2016-01-01 is the week 1 of 2016, but also is the week 53 of 2015.
When I run SELECT DATE_PART(w, '2016-01-01') it returns 53, but when I run SELECT DATE_PART(w, '2016-01-04') it returns 1.
Most probably this is happening because Redshift sets Monday as the day 1 of the week, and not Sunday, as it should be.
Not sure if this will solve the problem, but what I need is to make 2016-01-01 as week 1 and from 2016-01-03 to 2016-01-09 as week 2 and so on...
Create a custom function with this case statement or use it in your SQL statements:
case when DATE_PART('W', dt)>=51 and EXTRACT(DOY FROM dt) < 8 then 1 else DATE_PART('W', dt)+1 end week_no
It will start counting weeks on Jan 1st.
http://sqlfiddle.com/#!15/ccb90/9
Related
I'm used to do the following syntax when analysing weekly data:
select week(creation_date)::date as week,
count(*) as n
from table_1
where creation_date > current_date - 30
group by 1
However, by doing this I will get just part of the first week.
Is there any smart way to alway get a whole week in the beginning?
Like get the first day of the week I would get half of.
First off you need to define what you mean by "week". This is more difficult than it appears. While humans have an intuitive since of a week, computers are just not that smart. There are 2 common conventions: the ISO-8601 Standard and, for lack of a better term, Traditional. ISO-8601 defines a week as always beginning on Monday and always containing 7 days. Traditional weeks begin on Sunday (usually) but may have weeks with less than 7 days. This results from having the 1st week of the year beginning on 1-Jan regardless of day of week. Thus the 1st and/or last weeks may have less than 7 days. ISO-8601 throws it own curve into the mix: the 1st week of the year begins on the week containing 4-Jan. Thus the last days of Dec may be in week 1 of the next year and the first days Jan may be in week 52/53 of the prior year.
All the below assume the ISO-8061.
Secondly there is no week function in Postgres. In you need extract function. So for this particular case:
select extract(week from creation_date)::integer as week, ...
Finally, your predicate (current_date - 30) ensures you will unusually not begin on the 1st of the week. To get the correct date take that result back 1 week, then go forward to the next Monday.
with days_to_monday (day_adj) as
( values ('{7,6,5,4,3,2,1}'::int[]) )
select current_date - 30
, current_date - 30 - 7 + day_adj[extract (isodow from current_date - 30 )]
from table_1 cross join days_to_monday;
The CTE establishes an array which for a given day of the week contains the number of days need to the next Monday. That main query extracts the day of week of current date and uses that to index the array. The corresponding value is added to get the proper date.
Putting that together with your original query to arrive at:
with next_week (monday) as
( values (current_date - 30 - 7
+ ('{7,6,5,4,3,2,1}'::int[])[extract (isodow from current_date - 30 )])
)
select extract(week from creation_date) as week,
count(*) as n
from table_1
where creation_date >= (select monday from next_week)
group by 1
order by 1;
For full example see fiddle.
Postgresql 8.4.
I'm new to this concept so if people could teach me I'd appreciate it.
For Obamacare, anyone that works 30 hours per week or more must be offered the same healthcare as is offered to any other worker. We can't afford that so we have to limit work hours for temp and part-timers. This is affecting the whole country.
I need to calculate the hours worked (doesn't matter if overtime,
regular time, double time, etc) between two dates, say Jan 1, 2014,
and Nov 1, 2014 (Saturday) for each custom week (which beings on Sunday), not the week as defined by Postgresql (which begins on Monday).
Each of my custom work weeks begins on Sunday and ends on Saturday.
I don't know if I have to include weeks where
they did not work at all in the average, but let's assume I do. Zero hours that week would draw down the average.
Table name is 'employeetime', date field is 'employeetime.stopdate', hours worked per day is in the field 'employeetime.hours', employeeid field is 'employeetime.empid'.
I'd prefer to do this in one query per employee and I will execute the query once per employee as I loop through employees. If not I'm open to suggestions. But I'd like to understand the SQL presented in the answer.
Currently EXTRACT(week from '2014-01-01') calculates the start of the week as a Monday, so that doesn't work for me. Link here.
How would I do that without doing, say a separate query for each week, per person? We have 200 people to process.
Thank you.
I have set up a table to match your format:
select * from employeetime order by date;
id date hours
1 2014-11-06 10
1 2014-11-07 3
1 2014-11-08 5
1 2014-11-09 3
1 2014-11-10 5
You can get the week starting on Sunday by shifting. Note, here the 9th is a Sunday, so that is where we want the boundary.
select *, extract(week from date + '1 day'::interval) as week
from employeetime
order by week;
id date hours week
1 2014-11-07 3 45
1 2014-11-06 10 45
1 2014-11-08 5 45
1 2014-11-09 3 46
1 2014-11-10 5 46
And now the week shifts on Sunday rather than Monday. From here, the query to get hours by week/employee would be simple:
select id, sum(hours) as hours, extract(week from date + '1 day'::interval) as week
from employeetime
group by id, week
order by id, week;
id hours week
1 18 45
1 8 46
I'm using SAS 9.3
I need to create a way to sum up by week total, and I have no idea how to do it. So basically I have a year list of dates (left column below) with a total from that date (the right column). Our week goes from Friday to the previous Thursday (e.g. Thursday Oct 17 through Friday the Oct 25th).
An issue I also have is as you see the dates on the left are not completely daily and don't always have a Thursday date before the last Friday date. Would any know a way to add these weeks up - Week 1, Week 2, etc etc ...?
Thanks for any help that can be provided
2013-01-01 3
2013-01-02 8
2013-01-03 8
2013-01-04 10
2013-01-06 1
2013-01-07 10
2013-01-08 14
2013-01-09 12
2013-01-10 8
2013-01-11 9
2013-01-12 1
2013-01-14 12
2013-01-15 8
2013-01-16 5
2013-01-17 15
2013-01-18 7
2013-01-20 1
Trivial way:
data want;
set have;
weekno = ceil((date-'03JAN2013'd)/7);
run;
IE, subtract the first thursday and divide by 7, (so 1/1-1/3 is weekno=0).
INTCK function is also adept at calculating this. The basic structure is
weekno=intck('WEEK.5','04JAN2013'd,date); *the second argument is when you want your first week to start;
WEEK means calculate weeks, # on left side of decimal is multiple week groups (2 week periods is WEEK2.), on right side is shift index (from the default sunday-saturday week).
You could also create a format that contained your weeks, and use that.
NEWBIE at work! I am trying to create a simple summary that counts the number of customer visits and groups by 1) date and 2) hour, BUT outputs this:
Date Day of Wk Hour #visits
8/12/2013 Monday 0 5
8/12/2013 Monday 1 7
8/12/2013 Monday 6 10
8/13/2013 Tuesday 14 25
8/13/2013 Tuesday 16 4
We are on military time, so 14 = 2:00 pm
Select
TPM300_PAT_VISIT.adm_ts as [Date]
,TPM300_PAT_VISIT.adm_ts as [Day of Week]
,TPM300_PAT_VISIT.adm_ts as [Hour]
,count(TPM300_PAT_VISIT.vst_ext_id) as [Total Visits]
From
TPM300_PAT_VISIT
Where
TPM300_PAT_VISIT.adm_srv_cd='22126'
and TPM300_PAT_VISIT.adm_ts between '07-01-2013' and '08-01-2013'
Group by
cast(TPM300_PAT_VISIT.adm_ts as DATE)
,datepart(weekday,TPM300_PAT_VISIT.adm_ts)
,datepart(hour,TPM300_PAT_VISIT.adm_ts)
Order by
CAST(TPM300_PAT_VISIT.adm_ts as DATE)
,DATEPART(hour,TPM300_PAT_VISIT.adm_ts)
This should solve the problem:
; With Streamlined as (
SELECT
DATEADD(hour,DATEDIFF(hour,'20010101',adm_ts),'20010101') as RoundedTime,
vst_ext_id
from
TPM300_PAT_VISIT
where
adm_srv_cd='22126' and
adm_ts >= '20130701' and
adm_ts < '20130801'
)
Select
CONVERT(date,RoundedTime) as [Date],
DATEPART(weekday,RoundedTime) as [Day of Week],
DATEPART(hour,RoundedTime) as [Hour],
count(vst_ext_id) as [Total Visits]
From
Streamlined
Group by
RoundedTime
Order by
CONVERT(date,RoundedTime),
DATEPART(hour,RoundedTime)
In the CTE (Streamlined)'s select list, we floor each adm_ts value down to the nearest hour using DATEADD/DATEDIFF. This makes the subsequent grouping easier to specify.
We also specify a semi-open interval for the datetime comparisons, which makes sure we include everything in July (including stuff that happened at 23:59:59.997) whilst excluding events that happened at midnight on 1st August. This is frequently the correct type of comparison to use when working with continuous data (floats, datetimes, etc), but means you have to abandon BETWEEN.
I'm also specifying the dates as YYYYMMDD which is a safe, unambiguous format. Your original query could have been interpreted as either January 7th - January 8th or 1st July - 1st August, depending on the settings of whatever account you use to connect to SQL Server. Better yet, if these dates are being supplied by some other (non-SQL) code, would be for them to be passed as datetimes in the first place, to avoid any formatting issues.
The MS SQL DateDiff function counts the number of boundaries crossed when calculating the difference between two dates.
Unfortunately for me, that's not what I'm after. For instance, 1 June 2012 -> 30 June 2012 crosses 4 boundaries, but covers 5 weeks.
Is there an alternative query that I can run which will give me the number of weeks that a month intersects?
UPDATE
To try and clarify exactly what I'm after:
For any given month I need the number of weeks that intersect with that month.
Also, for the suggestion of just taking the datediff and adding one, that won't work. For instance February 2010 only intersects with 4 weeks. And the DateDiff calls returns 4, meaning that simply adding 1 would leave me the wrong number of weeks.
Beware: Proper Week calculation is generally trickier than you think!
If you use Datepart(week, aDate) you make a lot of assumptions about the concept 'week'.
Does the week start on Sunday or Monday? How do you deal with the transition between week 1 and week 5x. The actual number of weeks in a year is different depending on which week calculation rule you use (first4dayweek, weekOfJan1 etc.)
if you simply want to deal with differences you could use
DATEDIFF('s', firstDateTime, secondDateTime) > (7 * 86400 * numberOfWeeks)
if the first dateTime is at 2011-01-01 15:43:22 then the difference is 5 weeks after 2011-02-05 15:43:22
EDIT: Actually, according to this post: Wrong week number using DATEPART in SQL Server
You can now use Datepart(isoww, aDate) to get ISO 8601 week number. I knew that week was broken but not that there was now a fix. Cool!
THIS WORKS if you are using monday as the first day of the week
set language = british
select datepart(ww, #endofMonthDate) -
datepart(ww, #startofMonthDate) + 1
Datepart is language sensistive. By setting language to british you make monday the first day of the week.
This returns the correct values for feburary 2010 and june 2012! (because of monday as opposed to sunday is the first day of the week).
It also seems to return correct number of weeks for january and december (regardless of year). The isoww parameter uses monday as the first day of the week, but it causes january to sometimes start in week 52/53 and december to sometimes end in week 1 (which would make your select statement more complex)
SET DATEFIRST is important when counting weeks. To check what you have you can use select ##datefirst. ##datefirst=7 means that first day of week is sunday.
set datefirst 7
declare #FromDate datetime = '20100201'
declare #ToDate datetime = '20100228'
select datepart(week, #ToDate) - datepart(week, #FromDate) + 1
Result is 5 because Sunday 28/2 - 2010 is the first day of the fifth week.
If you want to base your week calculations on first day of week is Monday you need to do this instead.
set datefirst 1
declare #FromDate datetime = '20100201'
declare #ToDate datetime = '20100228'
select datepart(week, #ToDate) - datepart(week, #FromDate) + 1
Result is 4.