PgSQL turning day-of-year back into date - postgresql

I am trying to determine how to turn a day-of-year back into a date in PgSQL. When I do this
select date '2013-01-01' + interval '53 days'
I get a timestamp:
"2013-02-23 00:00:00"
So how come when I do any of the following
select extract(date from (date '2013-01-01' + interval '53 days'))
select extract(date from (select date '2013-01-01' + interval '53 days'))
I get "ERROR: timestamp units "date" not recognized"? Besides the why, how can I do what I want, which is to only get the date portion of the result of the original operation?

Use
select (date '2013-01-01' + interval '53 days')::date
or
select cast(date '2013-01-01' + interval '53 days' as date)
PostgreSQL's standard SQL function "extract()" will operate on timestamps, but a) "date" isn't a valid argument to extract(), and b) it returns subfields, not a collection of subfields. Conceptually, a date consists of a collection of three subfields: year, month, and day.
select extract(year from current_timestamp),
extract(month from current_timestamp),
extract(day from current_timestamp),
-- Concatenate and cast to type "date".
(extract(year from current_timestamp) || '-' ||
extract(month from current_timestamp) || '-' ||
extract(day from current_timestamp))::date

Related

Generate dates for postgres

i have a table
and i have a range from '2019-01-02' to '2019-01-04'
I need to generate ID and DATES (generated) from my table which started_at and ended_at (nullable) between the given range
result must be like this:
ID 4 from table is not included in result because it's started_at and ended_at not in range '2019-01-02' and '2019-01-04'
I need query that will do that in postgres.
Use generate_series()
select t.id, g.dt::date
from the_table t
cross join generate_series(t.started_at::date + 1,
least(t.ended_at::date, date '2019-01-04'),
interval '1 day') as g(dt)
where t.started_at >= date '2019-01-02'
and t.started_at < date '2019-01-04';
Worked this variant:
select t.id, g.dt::date from the_table t
cross join generate_series(t.started_at::date + 1,
least(t.ended_at::date, date '2019-01-04'), interval '1 day') as g(dt)
where dt >= date '2019-01-02' and dt < date '2019-01-04';

Compare day in current month to same day previous month PostgreSQL

I'm trying to compare values of current month's data to previous months using PostgreSQL. So if today is 4/23/2018, I want the data for 3/23/2018.
I've tried current_date - interval '1 month' but it is problematic for months with 31 days.
My table is structured as simply as
date, value
Check this example query:
WITH dates AS (SELECT date::date FROM generate_series('2018-01-01'::date, '2018-12-31'::date, INTERVAL '1 day') AS date)
SELECT
start_dates.date AS start_date,
end_dates.date AS end_date
FROM
dates AS start_dates
RIGHT JOIN dates AS end_dates
ON ( start_dates.date + interval '1 month' = end_dates.date AND
end_dates.date - interval '1 month' = start_dates.date);
It will output all end_dates and corresponding start_dates. The corresponding dates are defined by interval '1 month' and checked in both ways:
start_dates.date + interval '1 month' = end_dates.date AND
end_dates.date - interval '1 month' = start_dates.date
The output looks like this:
....
2018-02-26 2018-03-26
2018-02-27 2018-03-27
2018-02-28 2018-03-28
2018-03-29
2018-03-30
2018-03-31
2018-03-01 2018-04-01
2018-03-02 2018-04-02
2018-03-03 2018-04-03
2018-03-04 2018-04-04
....
Note, that there are 'gaps' for days without corresponding dates.
Back to your table, join the table with itself (giving aliases) and use given join condition, so the query would look like this:
SELECT
start_dates.value - end_dates.value AS change,
start_dates.date AS start_date,
end_dates.date AS end_date
FROM
_your_table_name_ AS start_dates
RIGHT JOIN _your_table_name_ AS end_dates
ON ( start_dates.date + interval '1 month' = end_dates.date AND
end_dates.date - interval '1 month' = start_dates.date);
Given the following table structure:
create table t (
d date,
v int
);
After populating with some dates and values, there is a way to find the value of the previous month using simple calculations and the LAG function, without resorting to joins. I am not sure how it compares from a performance perspective, so please run your own tests before selecting which solution to use.
select
*,
lag(v, day_of_month) over (order by d) as v_end_of_last_month,
lag(v, last_day_of_previous_month + day_of_month - cast(extract(day from d - interval '1 month') as int)) over (order by d) as v_same_day_last_month
from (
select
*,
lag(day_of_month, day_of_month) over (order by d) as last_day_of_previous_month
from (
select
*,
cast(extract(day from d) as int) as day_of_month
from
t
) t_dom
) t_dom_ldopm;
You may note that between the 29th and 31st of March, the comparison will be made against the 28th of February, since the same day does not exist in February for those particular dates. The same logic applies to other months with different number of days.

Creating a PostgreSQL `tsrange` from two timestamps

I am trying to create a tsrange (last Thursday to the previous Thursday) in a postgresql query but I get cast errors.
This is what I have got so far (starting off from this SO question).
WITH past_week AS (
SELECT date_trunc('day', NOW() + (s::TEXT || ' day')::INTERVAL)::TIMESTAMP(0) AS day
FROM generate_series(-7, 0, 1) AS s)
SELECT (
date_trunc('day', (SELECT day FROM past_week WHERE EXTRACT(DOW FROM day) = '4') - '7 day'::INTERVAL),
date_trunc('day', (SELECT day FROM past_week WHERE EXTRACT(DOW FROM day) = '4')));
And this is the result (correct value, but not format, since it's not a range):
row
-----------------------------------------------
("2015-10-29 00:00:00","2015-11-05 00:00:00")
(1 row)
Now, there are 2 main things that bug me:
If I try and add a ::tsrange right before the end of the query, the interpreter complains that:
ERROR: cannot cast type record to tsrange
LINE 6: ...ROM past_week WHERE EXTRACT(DOW FROM day) = '4')))::tsrange;
I would love to avoid repetition, but I'm not that proficient in SQL to know how. Any improvement is more than welcome.
Use tsrange() constructor:
WITH past_week AS (
SELECT date_trunc('day', NOW() + (s::TEXT || ' day')::INTERVAL)::TIMESTAMP(0) AS day
FROM generate_series(-7, 0, 1) AS s)
SELECT tsrange(
date_trunc('day',
(SELECT day FROM past_week
WHERE EXTRACT(DOW FROM day) = '4') - '7 day'::INTERVAL),
date_trunc('day',
(SELECT day FROM past_week
WHERE EXTRACT(DOW FROM day) = '4')));
tsrange
-----------------------------------------------
["2015-10-29 00:00:00","2015-11-05 00:00:00")
(1 row)
Using CURRENT_DATE your query may be as simple as:
WITH previous_thursday AS (
SELECT CURRENT_DATE- EXTRACT(DOW FROM CURRENT_DATE)::int+ 4 AS thursday
)
SELECT tsrange(thursday- '7d'::INTERVAL, thursday)
FROM previous_thursday;

How define today date with Default timestmp

I am using postgressql i wish to get the data for currentdate, i want filter the data based on the date
in data base my plandate filed is define as Time stamp with time zone so its showing like this format 2013-09-01 03:22:01.438348+05:30 my query is like this
select ttodoid ,date,details from ttodo where date=currentdate():
but currentdate function giving me just date '2013-10-06' based on this result is no rows how can i manage it for today date detail
UPDATED: One way to do it
SELECT *
FROM ttodo
WHERE date BETWEEN DATE_TRUNC('day', CURRENT_TIMESTAMP)
AND DATE_TRUNC('day', CURRENT_TIMESTAMP)
+ INTERVAL '1 DAY'
- INTERVAL '1 MICROSECOND';
or
SELECT *
FROM ttodo
WHERE date >= DATE_TRUNC('day', CURRENT_TIMESTAMP)
AND date < DATE_TRUNC('day', CURRENT_TIMESTAMP)
+ INTERVAL '1 DAY';
Here is SQLFiddle demo
select * from ttodo where (ttodo.todoplandate::date = current_date) or
(ttodo.todoplandate::date < current_date
I think the easier approach would be just to convert your field to date:
SELECT ttodoid ,date,details FROM ttodo
WHERE CAST(date AS DATE) = current_date;
Notice that, ff you want this query to be indexed, you have to create the index with the cast:
CREATE INDEX idx_ttodo_date ON ttodo ((CAST(date AS DATE)));
Another approach, is instead of casting the field, is checking the intervals, something similar of what petern proposed, but with correct intervals:
SELECT ttodoid ,date,details FROM ttodo
WHERE date >= date_trunc('day', current_timestamp)
AND date < (date_trunc('day', current_timestamp) + interval '1day');
This approach has the advantage that it can use an index on the date field only, which is good if you already have it.

PostgreSQL how to concat interval value '2 days'

In PostgreSQL I want to concat the current_timestamp with an interval as follows:
select current_timestamp + interval 2||' days'
But when I do, I get an error:
[Err] ERROR: syntax error at or near "2"
LINE 1: select current_timestamp + interval 2||' days'
But if I do it like this, it works correctly:
select current_timestamp + interval '2 days'
Why does one work, but not the other?
With reference to the following page
http://www.postgresql.org/docs/8.0/static/functions-datetime.html
Part of the problem is that the standard SQL expression for intervals quotes the number, but not the keywords. So you have to be careful.
select current_date, current_date + interval '2' day;
--
2012-02-21 2012-02-23 00:00:00
In PostgreSQL, quoting like '2 day' and '2 days' also works. So you might think that '2' || ' days' would be equivalent, but it's not.
select current_date, current_date + interval '2' || ' days';
--
2012-02-21 2012-02-21 00:00:02 days
The solution, as A.H. said, is to cast the result string as an interval.
You can also use a variable in place of 2. This generates a calendar for 2012.
-- 0 to 365 is 366 days; 2012 is a leap year.
select ('2012-01-01'::date + (n || ' days')::interval)::date calendar_date
from generate_series(0, 365) n;
I use that final cast to date, because date + interval returns a timestamp.
Please try this syntax:
select current_timestamp + ( 2 || ' days')::interval;
or even this one:
select current_timestamp + 2 * interval '1 day';
I use this
SELECT now() + (Var1 || ' ' || Var2)::interval
no concact function