I am using PostgreSQL 8.3. I have a table like this:
id regist_time result
-----------------------------------
1 2012-07-09 15:00:08 3
2 2012-07-25 22:24:22 7
4 2012-07-07 22:24:22 8
regist_time's data type is timestamp.
I need to find a week time interval(start to end)
and sum(result) as num.
I want to get the result as:
week num
---------------------------------
7/1/2012-7/7/2012 10
7/8/2012-7/14/2012 5
7/15/2012-7/21/2012 3
7/22/2012-7/28/2012 11
I can get the week number just in this year:
SELECT id,regis_time, EXTRACT(WEEK FROM regis_time) AS regweek
FROM tba
The key part is
EXTRACT(WEEK FROM regis_time)
extract function can only get the week number in this year, how can I get start time to end time in one week?
You can use date_trunc('week', ...).
For example:
SELECT date_trunc('week', '2012-07-25 22:24:22'::timestamp);
-> 2012-07-23 00:00:00
Then, you can convert this into a date, if you're not interested in a start time.
To get the end date too:
SELECT date_trunc('week', '2012-07-25 22:24:22'::timestamp)::date
|| ' '
|| (date_trunc('week', '2012-07-25 22:24:22'::timestamp)+ '6 days'::interval)::date;
-> 2012-07-23 2012-07-29
(I've used the default formatting here, you can of course adapt this to use MM/DD/YYYY.)
Note that, if you want to make comparisons on timestamps, instead of using (date_trunc('week', ...) + '6 days'::interval, you might want to add an entire week and use a strict comparison for the end of the week.
This will exclude y timestamps on the last day of the week (since the cut-off time is midnight on the day).
date_trunc('week', x)::date <= y::timestamp
AND y::timestamp <= (date_trunc('week', x) + '6 days'::interval)::date
This will include them:
date_trunc('week', x)::date <= y::timestamp
AND y::timestamp < (date_trunc('week', x) + '1 week'::interval)
(That's in the rare cases when you can't use date_trunc on y directly.)
If your week starts on a Sunday, replacing date_trunc('week', x)::date with date_trunc('week', x + '1 day'::interval)::date - '1 day'::interval should work.
select date_trunc('week', regist_time)::date || ' - ' ||
(date_trunc('week', regist_time) + '6 days') ::date as Week,
sum(result) Total
from YourTable
group by date_trunc('week', regist_time)
order by date_trunc('week', regist_time)
See proof of concept at SQLFiddle: http://sqlfiddle.com/#!1/9e821/1
This can help, a query to get all days of current week.
select cast(date_trunc('week', current_date) as date) + i
from generate_series(0,6) i
2015-08-17
2015-08-18
2015-08-19
2015-08-20
2015-08-21
To get week start and end date (as 0 for Monday and 4 for Friday):
select cast(date_trunc('week', current_date) as date) + 0 || '-->' || cast(date_trunc('week', current_date) as date) + 4;
2015-08-17-->2015-08-21
It is
select to_date('2015-07', 'IYYY-IW');
use it in postgres
it will return
2015-02-09
If you want to get week start and end date with week start day is any day in the week (Monday, Tuesday, ...). You can use this way:
day_of_week_index mapping:
{
'monday': 1,
'tuesday': 2,
'wednesday': 3,
'thursday': 4,
'friday': 5,
'saturday': 6,
'sunday': 7
}
Query template:
SELECT
concat(
CASE
WHEN extract(ISODOW FROM datetime_column) < day_of_week_index THEN cast(date_trunc('week', datetime_column) AS date) - 8 + day_of_week_index
ELSE cast(date_trunc('week', datetime_column) AS date) - 1 + day_of_week_index
END, ' - ',
CASE
WHEN extract(ISODOW FROM datetime_column) < day_of_week_index THEN cast(date_trunc('week', datetime_column) AS date) - 8 + day_of_week_index + 6
ELSE cast(date_trunc('week', datetime_column) AS date) - 1 + day_of_week_index + 6
END)
FROM table_name;
Example:
Get week start and end date with week start day is Tuesday:
SELECT
concat(
CASE
WHEN extract(ISODOW FROM TIMESTAMP '2021-12-01 03:00:00') < 2 THEN cast(date_trunc('week', TIMESTAMP '2021-12-01 03:00:00') AS date) - 8 + 2
ELSE cast(date_trunc('week', TIMESTAMP '2021-12-01 03:00:00') AS date) - 1 + 2
END, ' - ',
CASE
WHEN extract(ISODOW FROM TIMESTAMP '2021-12-01 03:00:00') < 2 THEN cast(date_trunc('week', TIMESTAMP '2021-12-01 03:00:00') AS date) - 8 + 2 + 6
ELSE cast(date_trunc('week', TIMESTAMP '2021-12-01 03:00:00') AS date) - 1 + 2 + 6
END);
=> Result:
2021-11-30 - 2021-12-06
Note: You can change the day_of_week_index following the above mapping to determine the week start day (Monday, Tuesday, ..., Sunday)
Related
I'm trying to generate dynamic dates based on the current date. I want to use generate_series() to populate dates between start and end dates (interval = 1 day).
If current date is before 10/1, start date is 10/1 in previous year
If current date is after 10/1, start date is 10/1 in the current year
end date is 9/30 in year 4. For example,
current date = 5/22/2019 -> start date = 10/1/2018, end date = 9/30/2021
current date = 11/1/2019 -> start date = 10/1/2019, end date = 9/30/2022
select generate_series(
to_date(cast(start_date as text), 'yyyy-mm-dd'),
to_date(concat(extract(year from to_date(cast(start_date as text), 'yyyy-mm-dd')+3),'-','09','-', 30), 'yyyy-mm-dd'),
'1 day'
)
from (
select case
when extract(month from current_date) <= 10 then concat(extract(year from current_date) -1,'-',10,'-', '01')
when extract(month from current_date) > 10 then concat(extract(year from current_date),'-',10,'-', '01')
end) as start_date
ERROR: invalid value "(202" for "yyyy"
DETAIL: Value must be an integer.
SQL state: 22007
It's complaining about year isn't integer. Which parts do I need to modify to run this query?
select case
when date_trunc('month', current_date) ::date < make_date(extract(year from current_date) ::int, 10, 1) then
generate_series(make_date((extract(year from current_date) - 1) ::int, 10, 1)
,make_date((extract(year from current_date) + 2) ::int, 10, 1) - 1
,'1 day') ::date
else
generate_series(make_date(extract(year from current_date) ::int, 10, 1)
,make_date((extract(year from current_date) + 3) ::int, 10, 1) - 1
,'1 day') ::date
end as dt;
Here In place of current_date you can use as below: current_date => '11/1/2019'::date or '05/22/2019'::date
I have below table
ABC Date
200 2019-02-22
-200 2019-02-23
1200 2019-02-24
-500 2019-02-25
'
'
'
'
-889 2015-01-11
I need to get values for from ABC for every day of last week of every month
select ABC
from table 1
where date between '2019-03-26' and '2019-03-30'
this is for month of march 2019. How do i create a loop such that it displays value for everyday of last week of every month for 3 years
You can use date arithmetic to get the last week of each month. In Terdata, I think this is one solution:
select abc
from table1
where (date >= (current_date - extract(day from date) * interval '1 day') - interval '6 day' and
date <= current_date - extract(day from date) * interval '1 day'
) or
(date >= (current_date - extract(day from date) * interval '1 day') - interval '1 month' - interval '6 day' and
date <= current_date - extract(day from date) * interval '1 day' - interval '1 month'
) or
(date >= (current_date - extract(day from date) * interval '1 day') - interval '2 month' - interval '6 day' and
date <= current_date - extract(day from date) * interval '1 day' - interval '12month'
);
SELECT ABC, DATE FROM table_1 WHERE DATEPART(wk, DATE) =
DATEPART(wk, EOMONTH(DATE)) AND DATE <= DATEADD(year,3,GETDATE())
DATEPART(wk, DATE) gives me the week number of that date, DATEPART(wk,EOMONTH(DATE)) gives me the week number of (the last day of the corresponding date's month). So, when I check this, I will only select dates belonging to the last week of every month. The next filter is to select only those dates which are lesser than 3 years from now (GETDATE()).
I found that there is a function last_day for last day of month, and date_part(dow, date) for numeric day of week starting with Sunday, but I am trying to take a date, and get the first day of that week.
Meaning: if date='2018-02-14' then result should be '2018-02-11'.
Any ideas?
You simply want to subtract the dow value from the current date.
select dateadd(d, -datepart(dow, my_date), my_date)
from (select date('2018-02-14') as my_date)
> 2018-02-11 00:00:00.0
For example, if dow is 3 for 2018-02-14 - a Wednesday - you can subtract 3 days to get back to "day 0".
There's also the date_trunc function which will truncate everything after a given datepart. This is a little clunky, and will only set you back to the previous Monday, not Sunday.
select date_trunc('week', my_date)
from (select date('2018-02-14') as my_date)
Not sure if there is a more efficient solution but:
date_trunc('week',my_date + '1 day'::interval)::date - '1 day'::interval as week_start
If you want to get First day of week with week start day is any day in the week (Monday, Tuesday, ...). You can use this way:
day_of_week_index mapping:
{
'monday': 1,
'tuesday': 2,
'wednesday': 3,
'thursday': 4,
'friday': 5,
'saturday': 6,
'sunday': 7
}
Query Template:
SELECT
CASE
WHEN extract(ISODOW FROM datetime_column) < day_of_week_index THEN cast(date_trunc('week', datetime_column) AS date) - 8 + day_of_week_index
ELSE cast(date_trunc('week', datetime_column) AS date) - 1 + day_of_week_index
END
FROM table_name;
Example:
Get First day of week with week start day is Wednesday
SELECT
CASE
WHEN extract(ISODOW FROM TIMESTAMP '2021-12-03 03:00:00') < 3 THEN cast(date_trunc('week', TIMESTAMP '2021-12-03 03:00:00') AS date) - 8 + 3
ELSE cast(date_trunc('week', TIMESTAMP '2021-12-03 03:00:00') AS date) - 1 + 3
END;
=> Result:
2021-12-01
Note: You can change the day_of_week_index following the above mapping to determine the week start day (Monday, Tuesday, ..., Sunday).
I used following query to get current week
select extract(week from current_timestamp) and it is showing 34 thats fine.
But how do i get the current week number of the current month.
You can find the first day of this week with:
trunc('week', current_date)
And the first day of the first week of this month with:
trunc('week', date_trunc('month', current_date))
Subtract the two to find the in-month weeknumber:
extract('day' from date_trunc('week', current_date) -
date_trunc('week', date_trunc('month', current_date))) / 7 + 1
Try the following
SELECT to_char('2016-05-01'::timestamp, 'W');
You can find week number by using day as well like:
select ((date_part('day', current_date)::integer - 1) / 7) +1;
Im not sure on how does this work on postgreSql (http://www.w3cyberlearnings.com/PostgreSQL_DATE_PART)
but in sql server the example is something like this...
>SELECT date_part('week', date)
This will give accurate results
CREATE OR REPLACE FUNCTION week_no(date)
RETURNS integer AS
$BODY$
SELECT
CASE WHEN EXTRACT(DAY FROM $1::TIMESTAMP)::INTEGER = 1
THEN 1
ELSE extract(week from $1:: TIMESTAMP)::integer
- extract(week from ( date_trunc('month', $1::TIMESTAMP) + interval '1 day' ) )::integer + 1
END
$BODY$
LANGUAGE sql IMMUTABLE STRICT
COST 100;
Example: week_no('2017-01-01') = 1
Maybe late.. but this works too
where cast(call_start as Date) = Date 'today'
or cast(call_start as Date)= Date 'today' -1
or cast(call_start as Date)= Date 'today' -2
or cast(call_start as Date)= Date 'today' -3
or cast(call_start as Date)= Date 'today' -4
or cast(call_start as Date)= Date 'today' -5
or cast(call_start as Date)= Date 'today' -6
or cast(call_start as Date)= Date 'today' -7
;
this gives me it
I'm trying to generate a series in PostgreSQL with the generate_series function. I need a series of months starting from Jan 2008 until current month + 12 (a year out). I'm using and restricted to PostgreSQL 8.3.14 (so I don't have the timestamp series options in 8.4).
I know how to get a series of days like:
select generate_series(0,365) + date '2008-01-01'
But I am not sure how to do months.
select DATE '2008-01-01' + (interval '1' month * generate_series(0,11))
Edit
If you need to calculate the number dynamically, the following could help:
select DATE '2008-01-01' + (interval '1' month * generate_series(0,month_count::int))
from (
select extract(year from diff) * 12 + extract(month from diff) + 12 as month_count
from (
select age(current_timestamp, TIMESTAMP '2008-01-01 00:00:00') as diff
) td
) t
This calculates the number of months since 2008-01-01 and then adds 12 on top of it.
But I agree with Scott: you should put this into a set returning function, so that you can do something like select * from calc_months(DATE '2008-01-01')
You can interval generate_series like this:
SELECT date '2014-02-01' + interval '1' month * s.a AS date
FROM generate_series(0,3,1) AS s(a);
Which would result in:
date
---------------------
2014-02-01 00:00:00
2014-03-01 00:00:00
2014-04-01 00:00:00
2014-05-01 00:00:00
(4 rows)
You can also join in other tables this way:
SELECT date '2014-02-01' + interval '1' month * s.a AS date, t.date, t.id
FROM generate_series(0,3,1) AS s(a)
LEFT JOIN <other table> t ON t.date=date '2014-02-01' + interval '1' month * s.a;
You can interval generate_series like this:
SELECT TO_CHAR(months, 'YYYY-MM') AS "dateMonth"
FROM generate_series(
'2008-01-01' :: DATE,
'2008-06-01' :: DATE ,
'1 month'
) AS months
Which would result in:
dateMonth
-----------
2008-01
2008-02
2008-03
2008-04
2008-05
2008-06
(6 rows)
Well, if you only need months, you could do:
select extract(month from days)
from(
select generate_series(0,365) + date'2008-01-01' as days
)dates
group by 1
order by 1;
and just parse that into a date string...
But since you know you'll end up with months 1,2,..,12, why not just go with select generate_series(1,12);?
In the generated_series() you can define the step, which is one month in your case. So, dynamically you can define the starting date (i.e. 2008-01-01), the ending date (i.e. 2008-01-01 + 12 months) and the step (i.e. 1 month).
SELECT generate_series('2008-01-01', '2008-01-01'::date + interval '12 month', '1 month')::date AS generated_dates
and you get
1/1/2008
2/1/2008
3/1/2008
4/1/2008
5/1/2008
6/1/2008
7/1/2008
8/1/2008
9/1/2008
10/1/2008
11/1/2008
12/1/2008
1/1/2009