How to pass a parameterized INTERVAL in a slonik/postgres query? - postgresql

This query seems to be legal when I run it in Datagrip with a parameter value of '14 days'
SELECT * FROM users WHERE users.updated_at < (CURRENT_DATE - INTERVAL $1)
But trying to do something similar in slonik as below does not:
const interval='14 days'
// ...
const {rows} = await pool.query<any>(sql`
SELECT * FROM users WHERE users.updated_at < (CURRENT_DATE - INTERVAL ${interval}))
`)
Seems to yield the same query: "SELECT * FROM users WHERE updated_at < CURRENT_TIME - INTERVAL $1)" but the pool's query method throws a syntax error near "$1" error when I try to execute it.
I've tried a couple of variations including escaping the input const interval="'14 days'" and adding parens for the INTERVAL function (CURRENT_DATE - INTERVAL(${interval})) with the same error results.
Is it not possible to parameterize a slonik query this way or am I doing something stupid?

You can subtract an integer representing the number of days from CURRENT_DATE because that is a date value not a timestamp
SELECT * FROM users WHERE updated_at < CURRENT_DATE - $1
Then pass $1 as an integer
Another option is to multiply an interval of a specific length with the parameter:
SELECT * FROM users WHERE updated_at < CURRENT_DATE - (interval '1 day' * $1)
or use the make_interval() function:
SELECT * FROM users WHERE updated_at < CURRENT_DATE - make_interval(days => $1)

Related

current_date in redshift exclude today's date when i am using with between command

I want to query data for last 30 days including today from redshift table. below is my query.
my date_column's type is 'timestamp without timezone'
select *
from mytable
WHERE date_column BETWEEN current_date - INTERVAL '30 day' AND current_date
order by date_column desc;
It gives the result for 30 days. But it doesn't include today's result.
I want to query for 30 days result including today's result also.
If it's a timestamp don't use between as it also compares the time part. Use a range query:
where date_column >= current_date - interval '30 day'
and date_column < current_date + interval '1 day'
Note that the upper bound is using < together with "tomorrow"
With Postgres this could be simplified to
where date_column >= current_date - 30
and date_column < current_date + 1
but Redshift isn't Postgres and I don't know if that would work there.

How to use COALESCE with INTERVAL in PostgreSQL?

This is a syntax error, but didn't see what the correct syntax would be.
The goal is to handle the case where days is null.
SELECT * FROM tbl WHERE created_at > current_date - INTERVAL 'COALESCE(%days%, 999) DAY'
You can multiply COALESCE(%days%, 999) to an interval of 1 day:
SELECT * FROM tbl
WHERE created_at > current_date - COALESCE(%days%, 999) * INTERVAL '1 DAY'
Use make_interval()
SELECT *
FROM tbl
WHERE created_at > current_date - make_interval(days:=COALESCE(%days%, 999));

expressions including functions for values in interval type for postgres

Is there a way in postgres db. to have expression inside interval type ? , e.g. I can execute a query -
select (timestamp '1970-01-01 00:00:00' + interval '1 second' * J0.C3) from T637 J0
Now , I need to be able to manipulate value of '1 second' dynamically , meaning '(1+2) seconds' fails for postgres , along with that the "value portion" value if it is getting derived via a function , and output of that function is some integer , I can't use that function in interval , e.g. interval func(args) seconds, how to achieve such dynamic "value portion" for "interval" in postgres db ?
interval '(1+2) seconds' is not a valid expression. However, interval '1 second' * 2 is valid.
So, to get the equivalent of '(m+n) seconds', one would would typically do either:
(interval '1 second' * m) + (interval '1 second' * n)
or
(m+n) * interval '1 second'
likewise, if there is a function that returns a numeric value and we want an interval of that magnitude (with units of seconds), the following could be used:
func(args) * (interval '1 second')
For this kind of "interval building", make_interval() is quite handy:
make_interval(secs => (1 + 2) * some_column);

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