How to parameterize a query using INTERVAL - postgresql

The below query is syntactically correct, but it's not returning anything. I'm trying to find all data older than ? months. I've been testing with 1 month, or even 1 day, but I don't get any data. Is there anything wrong with this query?
SELECT * FROM detail t
WHERE t.job_start_time < now() - (? * INTERVAL '1 MONTHS')

I prefer using make_interval():
SELECT *
FROM detail t
WHERE t.job_start_time < now() - make_interval(months => ?)

Related

How to find Last Week entries and This Week entries from postgres tables

I want to find the LastWeek entries from postgres table with cycle from Monday to Sunday (both inclusive) For eg - if I query the data today i.e on 2020/07/26 (or say if i query data on any date between 2020/07/20 to 2020/07/26) i should get the data from 2020/07/13 to 2020/07/19
Query:
Select user, date_sent
from users
where date_sent between (SELECT current_date - cast(extract(dow from current_date) as int) - 6)
and (SELECT current_date - cast(extract(dow from current_date) as int) + 1)
Similarly I want to find the This Week entries week starting from Monday and ending on present date. For eg - If I query the data today i.e on 2020/07/26 I should get the data from 2020/07/20 to 2020/07/26. If i query on 2020/07/24 then I should get 2020/07/20 to 2020/07/24
Query:
select user, date_sent
from users
where date_sent >= date_trunc('week', current_date)
and date_sent <= date_trunc('day',current_date+1)
You are almost there.
For "this week":
select user, date_sent
from users
where date_sent >= date_trunc('week', current_date)
and date_sent < date_trunc('week', current_date) + interval '1 week';
For last week it's quite similar:
select user, date_sent
from users
where date_sent >= date_trunc('week', current_date) - interval '1 week'
and date_sent < date_trunc('week', current_date)
Your desired results are inconsistent. In your description, before your initial query you state:
if I query the data today i.e on 2020/07/26 (or say if i query data on
any date between 2020/07/20 to 2020/07/26) i should get the data from
2020/07/13 to 2020/07/19
But after that query you state:
If I query the data today i.e on 2020/07/26 I should get the data from
2020/07/20 to 2020/07/26.
You cannot have both.
Assuming the latter to be correct and assuming ISO-8601 week definition, then your request can be re-phased as:
Given a specified date, if that date falls in the same week as the
current date then return the dates from the start of the week to the
specified date, inclusive. If the specified date does not fall in the
current week return the dates return the dates from Monday on or prior
to the specified date through Sunday on or after the specified date, inclusive.
The following implements that.
with targets (for_week_containing_date
,from_week_start
,iso_from_week
,iso_this_week) as
( select &for_week_containing_date
, date_trunc('week', &for_week_containing_date)
, extract(week from &for_week_containing_date)
, extract(week from now())
)
select user, date_sent
from user_days
cross join targets
where 1=1
and date_sent >= from_week_start
and date_sent <= case when iso_from_week = iso_this_week
then for_week_containing_date
else from_week_start + interval '6 days'
end
;
Since I do not care much for substitution variables this would need bound variables from a script, or wrap wrap it in an SQL function. See example of that here. Also note the last 2 queries, make sure you are ok with and understand what's happening around year end. You may need to make end of year/ begin of year adjustments. The results are not from being in a function, but result from ISO-8601 definitions. End of year/Begin year checking is needed any time you deal with date ranges.

How to select data in 2 month(from now) use postgresql?

I'm new to sql and database work . Now I want select data in 2 month from now . the key is xxdate lookslike 2019-4-11
like:
select * from table where date > now() - 2 month
but I don't know the correct way to express it. can someone help?
You can use this query:
SELECT * FROM table WHERE date > (current_date - interval '2 month')::date;

Update time only if not a minute passed

I have created the next table.
-- TABLE user_time
user_id integer PRIMARY KEY,
prev_time TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW(),
total_time INTERVAL DEFAULT interval '0 second'
I have to add an interval value to the total_time, e.g.
total_time = total_time + NOW() - prev_time
only if not a minute passed since prev_time (so, less than 1 minute passed) in a single query.
The next construction is about what I want but it's wrong:
UPDATE user_time SET total_time = total_time +
(
SELECT NOW() - prev_time incinterval,
CASE
WHEN incinterval < interval '1 minute' THEN incinterval
ELSE interval '0 second'
END
FROM user_time WHERE user_id=6
)
Firstly the SELECT is wrong, PostgreSQL does not recognize incinterval in the CASE construction. Secondly there is the first extra column in SELECT which creates a pseudo name.
Do you have an idea how to correct the query OR
Is it the common practice to increment total time with the condition and store it to a database with a single query?
You can not expect Postgres to respect your alias like this inside a SELECT. Here's the way to go:
UPDATE user_time SET total_time = total_time +
(
SELECT
CASE
WHEN NOW() - prev_time < interval '1 minute'
THEN NOW() - prev_time
ELSE interval '0 second'
END
FROM user_time WHERE user_id=6
)
Or to use your alias:
UPDATE user_time SET total_time = total_time +
(
SELECT
CASE
WHEN incinterval < interval '1 minute'
THEN incinterval
ELSE interval '0 second'
END
FROM(
SELECT
NOW() - prev_time incinterval
FROM user_time WHERE user_id=6
) foo
)
Edit after comment:
Simply add , prev_time = NOW() after the last parenthesis in any option you choose from those above.
The deadlock issue can be resolved by doing the analysis post hoc. Just log the queries and "roll up" clusters that meet your interval spec. This allows for variable-length intervals, which can be very useful for sites that provide dense content.
The "post hoc" process can/should run periodically in overlapping ranges to catch longer intervals.
There is an relevant example of a generalizable temporal clustering query using analytic functions from about 8 years ago on asktom.oracle.com.

Date subtraction in postgres

I want to subtract minute from NOW() and the value of "how many minutes" I am reading from another table:
SELECT * FROM A, B
WHERE
A.entity_type_id = B.entity_type_id
AND A.status = 'PENDING'
AND A.request_time < (NOW() - INTERVAL B.retry_interval MINUTE)
AND A.retry_count >= B.retry_allowed_count
Here the problem is B.retry_interval is fetched from another table, while normally the queries like these are A.request_time < (NOW() - INTERVAL '10 MINUTE')
How do I achieve this?
Multiply the interval by interval '1 minute'
SELECT *
FROM A, B
WHERE
A.entity_type_id = B.entity_type_id
AND A.status = 'PENDING'
AND A.request_time < NOW() - B.retry_interval * INTERVAL '1 minute'
AND A.retry_count >= B.retry_allowed_count
I know its quite old enough, but here what i do. Btw my postgres version is 9.4.18.
try using interval data type. Convert the number from column to sting and concat it.
SELECT *
FROM A, B WHERE
A.entity_type_id = B.entity_type_id
AND A.status = 'PENDING'
AND A.request_time < (NOW() - concat(B.retry_interval::text,'minute')::interval
AND A.retry_count >= B.retry_allowed_count
Thanks to my coworker who find "interval" data type

Create date efficiently

On Pavel's page is the following function:
CREATE OR REPLACE FUNCTION makedate(year int, dayofyear int)
RETURNS date AS $$
SELECT (date '0001-01-01' + ($1 - 1) * interval '1 year' + ($2 - 1) * interval '1 day'):: date
$$ LANGUAGE sql;
I have the following code:
makedate(y.year,1)
What is the fastest way in PostgreSQL to create a date for January 1st of a given year?
Pavel's function would lead me to believe it is:
date '0001-01-01' + y.year * interval '1 year' + interval '1 day';
My thought would be more like:
to_date( y.year||'-1-1', 'YYYY-MM-DD');
Am looking for the fastest way using PostgreSQL 8.4. (The query that uses the date function can select between 100,000 and 1 million records, so it needs speed.)
Thank you!
I would just use the following, given that year is a variable holding the year, instead of using a function:
(year || '-01-01')::date
Btw. I can't believe that this conversion is your bottleneck. But maybe you should have a look at generate_series here (I don't know your usecase).
select current_date + s.a as dates from generate_series(0,14,7) as s(a);
dates
------------
2004-02-05
2004-02-12
2004-02-19
(3 rows)
Using to_date() is even simpler than you expect:
> select to_date('2008','YYYY');
to_date
------------
2008-01-01
(1 row)
> select to_date(2008::text,'YYYY');
to_date
------------
2008-01-01
(1 row)
Note that you still have to pass the year as a string, but no concatenation is needed.
As suggested by Daniel, in the unlikely case that this conversion is a bottleneck, you might prefer to precompute the function and store in a table. Eg:
select ynum, to_date( ynum ||'-01-01', 'YYYY-MM-DD') ydate
from generate_series(2000,2009) as ynum;
If there are a few years (and hence no need of indexes), you might even create the table dinamically for the scope of each query, with the new WITH.