Getting a weird result for ISO Date in Postgresql - postgresql

I have a table task_details. I need to select its weekly date from this table. I have used ISO Year & ISO Week to extract the weekly dates from this table as I want to extract its week number as 53 if its Dec,2015 then on the days 28 Dec,29 Dec,30 Dec,31 Dec and 1 Jan '16,2 Jan '16 since it should not separate to two different weeks for these sort of dates. The query I have used for ISO Year & ISO Week is given below.
select
name, id,
to_date(week || ' ' || yr, 'IW IYYY') week_date,
sum(worked_hours) worked_hours_per_week,
week, yr
from (
select
name, id,
newdate, hours worked_hours,
to_number(to_char(newdate, 'IW'), '99') week,
extract( year from newdate) yr
from task_details t
) sub
where worked_hours > 0
group by name, id, to_date(week || ' ' || yr, 'IW IYYY'), week, yr
order by yr, week
It is working fine for the weeks but then I am getting this weird result for one date for a record in the table. The table doesn't have the data for the year 2017.Also, the yr column is showing 2016 which is as desired but then the newdate column and week column is giving weird result. Why is this happening? How do I fix this ?
Here is the SQL fiddle for it :http://sqlfiddle.com/#!15/53abf/1

You should not mix week with year in the extract function as year is for the Gregorian calendar rather than the special ISO calendar.
See section 9.9.1 and comments about week.
to_number(to_char(newdate, 'IW'), '99') is effectively extract(week from newdate)
Changing the yr column to be extract(isoyear from newdate) solves your problem.
Adjusted SQL Fiddle

#gwaigh already cleared up your confusion of Gregorian and ISO year.
However, your query would be simpler and faster with date_trunc() to get the first day of each week:
SELECT name, id
, date_trunc('week', newdate)::date AS week_date
, extract(week FROM newdate)::int AS week
, extract(isoyear from newdate)::int AS isoyr -- must be ISO year to match
, sum(hours) AS worked_hours_per_week
FROM task_details
WHERE hours > 0
GROUP BY name, id, week_date, week, yr
ORDER BY week_date;
Also simplified your query.
SQL Fiddle.
Either way, if you work with timestamptz then year, week or date depend on your current timezone setting. (It can be a different year, depending on where you are right now.)

Related

Postgres query certain overlapping times on weekdays

Imagine you have a set of records for a certain period of time, for example produced products in a month.
Now you want to know how many of those products have been produced at 22 to 3 on Wednesdays and Fridays.
To get all products let's assume the query looks like:
SELECT count(id)
FROM products
WHERE timestamp BETWEEN to_timestamp($startOfMonth) AND to_timestamp($endOfMonth)
where $startOfMonth and $endOfMonth are placeholder values for some integer timestamps.
To get products of a certain time we can make use of "BETWEEN TIME" and add it to the query:
SELECT count(id)
FROM products
WHERE timestamp BETWEEN to_timestamp($startOfMonth) AND to_timestamp($endOfMonth)
AND CAST(timestamp AS time) BETWEEN TIME '3:00' AND TIME '22:00'
Next thing we do is adding the exclusive days we want to query:
SELECT count(id)
FROM products
WHERE timestamp BETWEEN to_timestamp($startOfMonth) AND to_timestamp($endOfMonth)
AND CAST(timestamp AS time) BETWEEN TIME '3:00' AND TIME '22:00'
AND EXTRACT(dow FROM timestamp) IN (3,5)
Everything works fine.
But now I want to query a time overlapping a day:
SELECT count(id)
FROM products
WHERE timestamp BETWEEN to_timestamp($startOfMonth) AND to_timestamp($endOfMonth)
AND CAST(timestamp AS time) BETWEEN TIME '22:00' AND TIME '12:00'
AND EXTRACT(dow FROM timestamp) IN (3,5)
and I don't receive any results.
How to run this query if the starting time is on another day than the ending time?
Thank you ;-)
BETWEEN TIME '3:00' AND TIME '22:00' seems to be the opposite of what you're asking in the question ("produced at 22 to 3").
I think you'll just have to split the time range and use an OR condition, since it crosses midnight. Assuming that "22 to 3" on a Wednesday, you actually mean "22 to midnight" on a Wednesday and "midnight to 3am" on a Thursday (same for Friday/Saturday)
You won't even need BETWEEN, since the lower/upper bounds will be midnight, when the day changes. Something like this should work:
SELECT count(id)
FROM products
WHERE timestamp BETWEEN to_timestamp($startOfMonth) AND to_timestamp($endOfMonth)
AND ((CAST(timestamp AS time) >= TIME '22:00'
AND EXTRACT(dow FROM timestamp) IN (3,5))
OR (CAST(timestamp AS time) < TIME '03:00'
AND EXTRACT(dow FROM timestamp) IN (4,6)))
You may need to adapt this a little if you want to include the timestamps that are on the first Thursday or Saturday of the month (before 3am) into the count for the previous month (if it makes sense for your report): a quick way to do this is to adapt the integer timestamp you provide $endOfMonth to be at 3am on the first day of the following month.
(As a side note, I'd avoid using timestamp as a column name, since it's also a type and can cause confusion.)
Perhaps an easier way is to restate the times in question. That is instead of working with TIMEs a 22:00 and 03:00 consider start time as date+22hours and end time as date+27 hours.(duration of 5 hours from 22:00 - 03:00) Then process with the full timestamp.
SELECT count(id)
FROM products
WHERE timestamp BETWEEN to_timestamp($startOfMonth) AND to_timestamp($endOfMonth)
AND EXTRACT(dow FROM timestamp) IN (3,5)
and timestamp between date_trunc('day', timestamp) + interval '22 hours'
and date_trunc('day', timestamp) + interval '27 hours';
Note: I echo #Bruno on not using timestamp as a column (or any DB object) name. While it is not a Postgres reserved word, Postgres restricts is use and it is a SQL Standard reserved word.

Generate days between dates in a generate series by month postgreSQL query

I have a query the following query :
SELECT EXTRACT(YEAR FROM t::date),
EXTRACT(MONTH FROM t::date),t::date+ interval '1 month'- t::date
FROM generate_series('2015-04-01', '2016-02-09', '1 month'::interval) t
The query gives the correct number of days for all the months, except for the month of Feb, 2016 ie, I expect to get 9 days and not 29 days as the result. What change should I make to the query above? Thanks

Have Datetable with dates and if business day, need to find the 11th business day after a date

I need to find a date that is 11 business days after a date.
I did not have a date table. Requested one, long lead time for one.
Used a CTE to produce results that have a datekey, 1 if weekday, and 1 if holiday, else 0. Put those results into a Table Variable, now Business_Day is (weekday-holiday). Much Googling has already happened.
select dt.Datekey,
(dt.Weekdaycount - dt.HolidayCount) as Business_day
from #DateTable dt[enter image description here][1]
UPDATE, I've figured it out in Excel. Running count of business days, a column of business day count + 11, then a Vlookup finding the +11 date . Now how do I do that in SQL?
Results like this
Datekey
2019-01-01
Business_day 0
Datekey
2019-01-02
Business_day
1
I will assume you want to set your weekdays, and you can enter the holidays in a variable table, so you can do the below:-
here set the weekend names
Declare #WeekDayName1 varchar(50)='Saturday'
Declare #WeekDayName2 varchar(50)='Sunday'
Set the holiday table variable, you may have it as a specific table your database
Declare #Holidays table (
[Date] date,
HolidayName varchar(250)
)
Lets insert a a day or two to test it.
insert into #Holidays values (cast('2019-01-01' as date),'New Year')
insert into #Holidays values (cast('2019-01-08' as date),'some other holiday in your country')
lets say your date you want to start from is action date and you need 11 business days after it
Declare #ActionDate date='2018-12-28'
declare #BusinessDays int=11
A recursive CTE to count the days till you get the correct one.
;with cte([date],BusinessDay) as (
select #ActionDate [date],cast(0 as int) BusinessDay
union all
select dateadd(day,1,cte.[date]),
case
when DATENAME(WEEKDAY,dateadd(day,1,cte.[date]))=#WeekDayName1
OR DATENAME(WEEKDAY,dateadd(day,1,cte.[date]))=#WeekDayName2
OR (select 1 from #Holidays h where h.Date=dateadd(day,1,cte.[date])) is not null
then cte.BusinessDay
else cte.BusinessDay+1
end BusinessDay
From cte where BusinessDay<#BusinessDays
)
--to see the all the dates till business day + 11
--select * from cte option (maxrecursion 0)
--to get the required date
select MAX([date]) from cte option (maxrecursion 0)
In my example the date I get is as below:-
ActionDate =2018-12-28
After 11 business days :2019-01-16
Hope this helps
1st step was to create a date table. Figuring out weekday verse weekends is easy. Weekdays are 1, weekends are 0. Borrowed someone else's holiday calendar, if holiday 1 else 0. Then Business day is Weekday-Holiday = Business Day. Next was to create a running total of business days. That allows you to move from whatever running total day you're current on to where you want to be in the future, say plus 10 business days. Hard coded key milestones in the date table for 2 and 10 business days.
Then JOIN your date table with your transaction table on your zero day and date key.
Finally this allows you to make solid calculations of business days.
WHERE CONVERT(date, D.DTRESOLVED) <= CONVERT(date, [10th_Bus_Day])

DB2- select rows with date_column of last month

I need to select count(field) from table where the date_column is within the last month, i.e. from the first of the last month till the first of this month. As this query will be automated it will need to refer to the last month of any given day.
Also the date_column has a datatype of timestamp
Thanks in advance!
SELECT (CAST(current timestamp AS DATE)) - (DAY(CAST(current timestamp AS DATE)) - 1)DAYS FROM sysibm.sysdummy1;
SELECT (CAST(current timestamp AS DATE)) - (DAY(CAST(current timestamp AS DATE)) - 1)DAYS
- 1 MONTH
FROM sysibm.sysdummy1;
Managed to find these queries, to find the first of this month and last month

Compute the number of pick-ups and drop-offs for each hour of the day and every day of the week from DB

I need to compute the number of pick-ups and drop-offs for each hour of the day and every day of the week from the below database.
I tried to apply below query but i guess it is very basic
select date, in_service from aid_atidivyakumarpatra;
click here to see the table
This should give you the results you are looking for. There are plenty of examples out there to give you a more verbose date that shows day of the week so I did not focus on that. The answer was in the Group by statement.
SELECT Convert(DATE, date) as Day, DATEPART(HOUR, date) as Hour,
COUNT (CASE inservice WHEN 'drop' THEN 1 ELSE null END) AS DropOffs,
COUNT (CASE inservice WHEN 'pick' THEN 1 ELSE null END) AS PickUps
FROM PickupDropoff
GROUP BY Convert(DATE, date), DATEPART(HOUR, date)
I made this answer in Microsoft SQL Server, if you are using Postgres hopefully there is an equivalent.