How to pass an interval parameter to a prepared statement? - postgresql

I want to delete all database entries in my Postgres database that are older than X minutes. My prepared statement in go looks like this:
delete
from my_table
where expires < (to_timestamp($1) - '$2 minutes'::interval);
How can I correctly pass the second parameter $2?
PS: I know there's different statements to solve the problem, but I am explicitly interested in how to pass parameters that are quoted.

There is no way to interpolate a parameter into a string literal.
A possible solution for your case would be to multiply a number with an interval:
where expires < (to_timestamp($1) - $2 * '1 minute'::interval)

You can cast the parameter to text and then concatenate it with the string ' minutes'.
delete from my_table
where expires < (to_timestamp($1) - ($2::text || ' minutes')::interval
UPDATE: actually, since postgres does have a any || text operator, the result of which is text, you shouldn't need to type cast the parameter.

You can use make_interval
delete from my_table
where expires < (to_timestamp($1) - make_interval(mins => $2));

You can also parametrize the entire interval string instead. This removes the need for an intermediate cast to text and concat.
query := `
delete from my_table
where expires < (to_timestamp($1) - $2::interval);
`
interval := fmt.Sprintf("%d minutes", mins)
db.Exec(query, timestamp, interval)

Related

case when in where clause postresql

select service_code,service_cat_code,mobile_no,upper(applicant_name_eng) as name,to_char(license_date,'dd/mm/yyyy')as license_from,to_char(license_valid_upto,'dd/mm/yyyy')as license_to,Upper(license_no),district_code,taluk_code,CONCAT(address_building,', ', address_cityvillage,', ',address_locality,', ',address_landmark,', ',address_street) as address
from mst_license
WHERE cast(license_valid_upto as date) = case
WHEN license_valid_upto < now()
THEN
case
when license_valid_upto = '2021-06-30'
then 1 else 0
END
ELSE
case when license_valid_upto > now()
then 1 else 0
End
END
and Upper(license_no)='1SP146924BJP'
I want license valid should be either greater than now or if license valid less than now it must be with the date ''30/06/2021' but when i use above query i get error
ERROR: operator does not exist: date = integer
LINE 3: WHERE cast(license_valid_upto as date) = case
^
HINT: No operator matches the given name and argument types. You might need to add explicit type casts.
SQL state: 42883
Character: 418
Help me out guys
The main issue you have is that your case statement returns an integer (1 or 0) but you are trying to compare that to a date, which you cannot do as Postgres is a strict data typing. Even if it did work it would always be false (well except for 1969-12-31/1970-01-01). Moreover the case structure is not needed. The best/correct to compare dates is just use date values. Since you did not indicate the data type for column license_valid_upto so based on how it is used I'll assume it is timestamp with timezone as that is what NOW() returns. Your query becomes:
select service_code
, service_cat_code
, mobile_no
, upper(applicant_name_eng) as name
, to_char(license_date,'dd/mm/yyyy')as license_from
, to_char(license_valid_upto,'dd/mm/yyyy')as license_to
, upper(license_no) as license_no
, district_code
, taluk_code
, concat(address_building,', '
,address_cityvillage,', '
,address_locality,', '
,address_landmark,', '
,address_street) as address
from mst_license
where and upper(license_no)='1SP146924BJP'
and ( cast(license_valid_upto as date) > cast( now() as date)
or (cast (icense_valid_upto as date) < cast( now() as date)
and cast (icense_valid_upto as date) = date '2021-06-30'
)
);
Also, learn for format your queries for readability and so you do not need to scroll right. You, and others looking at your queries later will appreciate it later.

Data factory Postgres query date time error

I have a postgressql query. I tried it in db query and it seems to work fine. But in data bricks its not working as expected.
I have to use where clause. Where I am extracting date from timestamp and comparing it with passed value (which is in date time format). I am extracting date and checking if its less than the one from timestamp.
Pls check in the WHERE clause.
select DATE(to_timestamp(time,'YYYY-MM-DD %H24:%m:%s' )) as modifiedtime, *
FROM testg
WHERE DATE(to_timestamp(time,'YYYY-MM-DD %H24:%m:%s' )) >= DATE(to_timestamp('2020-10-25 00:00:00','YYYY-MM-DD %H24:%m:%s' )) - INTERVAL '7 DAY'
order by time asc
In data factory the query looks like:
#concat('select * FROM streamingData WHERE property_name = ''', item(), '''' ,' AND DATE(to_timestamp(''', activity('Lookup1').output.firstRow.max_timestamp, '''',',''','YYYY-MM-DD %H24:%m:%s', ''' ))' ,' >= ''', 'DATE(to_timestamp(''', pipeline().parameters.enddate, '''',',''','YYYY-MM-DD %H24:%m:%s', ''' ))' )
Data:
time:
"2020-10-25 13:00:22.000000+01"
"2020-10-24 14:00:22.000000+01"
"2020-10-25 12:00:22.000000+01"
"2020-10-26 01:00:22.000000+01"
and I am using trigger().outputs.windowEndTime() -
output is: 2020-10-20 01:00:22
I am comparing both the date values i.e.
If 2020-10-26 >= 2020-10-20
Even after filtering, i get all the data i.e. also the dates from 2019 etc.
What am I missing.
Strangely I tried the comparison using Between (which was not working) and I was still getting all the years even after filtering.
Methods used :
Used between clause - was not working
Used <= and >= - was also not working.
I used later "SYMMETRIC" function. with between - with which the filter worked. I suppose in BETWEEN SYMMETRIC the arguments are automatically swapped and that a nonempty range is always implied.
at the end the query is like:
#concat('select * FROM streamingData WHERE property_name = ''', item(), '''' ,' AND time BETWEEN SYMMETRIC to_timestamp(''', activity('LKP_RMS_ValueStream_Time').output.firstRow.max_timestamp, '''',',''','YYYY-MM-DD', ''' )' ,' AND ', 'to_timestamp(''', pipeline().parameters.enddate, '''',',''','YYYY-MM-DD', ''' )' , ' - ', 'INTERVAL ''', '7 DAY', '''' )
Thanks for the time and help.

PostreSQL- How to convert float containing a number of seconds to MM:SS.sss

I have a float8 that contains a number of seconds ie 65.455. I was trying to format a column in a view so that it would read as 1:05.455.
Using postgres command like this:
TO_CHAR((user_data.totaltime || ' second')::interval, 'MI:SS')
I can format it as 1:05 but i loose the precision i need.
Anyone know of a way to achieve what i need ? It doesn't look like interval allows the formatting of milliseconds.
Try justify_interval:
SELECT justify_interval(
CAST(65.455::text || ' seconds' AS interval)
);
Here is a rather brute force you may do this, using the base string functions:
SELECT
FLOOR(65.455 / 60)::text || ':' || LPAD(FLOOR(65.455 % 60)::text, 2, '0') ||
TRIM(LEADING '0' FROM (65.455 % 1)::text);
This outputs:
1:05.455
Demo

concatenate arrays in aggregate query

i have a starting table where there are some meteo data stored every 15 minutes, one field stores leaf wet at 1 minute sampling in a numeric array form, thus i have a 15 values array each row.
Now i want to create a 1 hour aggregation of this table, crating an array of 60 values for this field.
I tried array_cat at first place, but says
array_cat(numeric[]) not existing
the function obviuously exists, so i tought the format was not the one expected, i tried first unnesting and then aggregating, not working again.
Finally i was able to aggregate trough string conversion, but it's not what i wanted (i might in the future apply some numeric elaboration oh that 60-values array)
I paste the query for further investigations
SELECT dati1_v.id_stazione,
to_char(dati1_v.data_ora, 'YYYY-MM-DD HH24:00:00'::text) AS date_hour,
round(avg(dati1_v.temp1_media), 2) AS t_avg,
round(avg(dati1_v.ur1_media), 2) AS hum_avg,
sum(dati1_v.pioggia) AS rain_tot,
max(dati1_v.pioggia) AS rain_max,
round((avg((SELECT avg(lw.lw) AS avg FROM unnest(dati1_v.lw_top_array) lw(lw))) - lws.top_min) /
(lws.top_max - lws.top_min) * 100::numeric, 2) AS lw_top_avg,
array_agg((SELECT round((avg(lw.lw) - lws.top_min) / (lws.top_max - lws.top_min) * 100::numeric, 2) AS round
FROM unnest(dati1_v.lw_top_array) lw(lw))) AS lw_top_array,
array_cat(dati1_v.lw_top_array) AS lw_top_array_tot,
-- array_agg((select lw_top_array from unnest(dati1_v.lw_top_array))) AS lw_top_array_tot,
-- array_agg(array_to_string(dati1_v.lw_top_array, ',')) AS lw_top_array_tot,
round((avg((SELECT avg(lw.lw) AS avg FROM unnest(dati1_v.lw_bottom_array) lw(lw))) - lws.bottom_min) /
(lws.bottom_max - lws.bottom_min) * 100::numeric, 2) AS lw_bottom_avg,
array_agg((SELECT round((avg(lw.lw) - lws.bottom_min) / (lws.bottom_max - lws.bottom_min) * 100::numeric,
2) AS round
FROM unnest(dati1_v.lw_bottom_array) lw(lw))) AS lw_bottom_array
FROM dati1_v,
lw_settings lws
WHERE lws.id = 1
GROUP BY dati1_v.id_stazione, to_char(dati1_v.data_ora, 'YYYY-MM-DD HH24:00:00'::text), lws.top_min, lws.top_max,
lws.bottom_min, lws.bottom_max
ORDER BY dati1_v.id_stazione, to_char(dati1_v.data_ora, 'YYYY-MM-DD HH24:00:00'::text)
in particular, my tries were related to this specific block:
array_cat(dati1_v.lw_top_array) AS lw_top_array_tot,
-- array_agg((select lw_top_array from unnest(dati1_v.lw_top_array))) AS lw_top_array_tot,
-- array_agg(array_to_string(dati1_v.lw_top_array, ',')) AS lw_top_array_tot
Thanks
For me in similar case helped UNNEST in subquery and ARRAY_AGG of unnnested
SELECT
ARRAY_AGG(
DISTINCT lw_top
) as lw_top_array
FROM (
SELECT
UNNEST(lw_top_array) AS lw_top
FROM
dati1_v
) as tmp;
for me helped next query
SELECT
my_table.key,
array_agg(_unnested.item) as array_coll
from my_table
left join LATERAL (SELECT unnest(my_table.array_coll) as item) _unnested ON TRUE
GROUP by my_table.key
In PostgreSQL, the Group_concat function is not available but you can get similar result as string_agg and array_to_string.
string_agg(array_to_string(file_ids, ','), ',') filter ( where file_ids notnull ) AS file_ids_str
array_to_string and array_to_string works in next way
array_to_string([1, 2, 456], ',') => '1,2,456'
string_agg(['a', 'ab'], ',') => 'a,ab'
the only problem is that result is string with ',' as separator

Convert a string representing a timestamp to an actual timestamp in PostgreSQL?

In PostgreSQL: I convert string to timestamp with to_timestamp():
select * from ms_secondaryhealthcarearea
where to_timestamp((COALESCE(update_datetime, '19900101010101'),'YYYYMMDDHH24MISS')
> to_timestamp('20121128191843','YYYYMMDDHH24MISS')
But I get this error:
ERROR: syntax error at end of input
LINE 1: ...H24MISS') >to_timestamp('20121128191843','YYYYMMDDHH24MISS')
^
********** Error **********
ERROR: syntax error at end of input
SQL state: 42601
Character: 176
Why? How to convert a string to timestamp?
One too many opening brackets. Try this:
select *
from ms_secondaryhealthcarearea
where to_timestamp(COALESCE(update_datetime, '19900101010101'),'YYYYMMDDHH24MISS') >to_timestamp('20121128191843','YYYYMMDDHH24MISS')
You had two opening brackets at to_timestamp:
where to_timestamp((COA.. -- <-- the second one is not needed!
#ppeterka has pointed out the syntax error.
The more pressing question is: Why store timestamp data as string to begin with? If your circumstances allow, consider converting the column to its proper type:
ALTER TABLE ms_secondaryhealthcarearea
ALTER COLUMN update_datetime TYPE timestamp
USING to_timestamp(update_datetime,'YYYYMMDDHH24MISS');
Or use timestamptz - depending on your requirements.
Another way to convert a string to a timestamp type of PostgreSql is the above,
SELECT to_timestamp('23-11-1986 06:30:00', 'DD-MM-YYYY hh24:mi:ss')::timestamp without time zone;
I had the same requirement as how I read the title. How to convert an epoch timestamp as text to a real timestamp. In my case I extracted one from a json object. So I ended up with a timestamp as text with milliseconds
'1528446110978' (GMT: Friday, June 8, 2018 8:21:50.978 AM)
This is what I tried. Just the latter (ts_ok_with_ms) is exactly right.
SELECT
data->>'expiration' AS expiration,
pg_typeof(data->>'expiration'),
-- to_timestamp(data->>'expiration'), < ERROR: function to_timestamp(text) does not exist
to_timestamp(
(data->>'expiration')::int8
) AS ts_wrong,
to_timestamp(
LEFT(
data->>'expiration',
10
)::int8
) AS ts_ok,
to_timestamp(
LEFT(
data->>'expiration',
10
)::int8
) + (
CASE
WHEN LENGTH(data->>'expiration') = 13
THEN RIGHT(data->>'expiration', 3) ELSE '0'
END||' ms')::interval AS ts_ok_with_ms
FROM (
SELECT '{"expiration": 1528446110978}'::json AS data
) dummy
This is the (transposed) record that is returned:
expiration 1528446110978
pg_typeof text
ts_wrong 50404-07-12 12:09:37.999872+00
ts_ok 2018-06-08 08:21:50+00
ts_ok_with_ms 2018-06-08 08:21:50.978+00
I'm sure I overlooked a simpler version of how to get from a timestamp string in a json object to a real timestamp with ms (ts_ok_with_ms), but I hope this helps nonetheless.
Update: Here's a function for your convenience.
CREATE OR REPLACE FUNCTION data.timestamp_from_text(ts text)
RETURNS timestamptz
LANGUAGE SQL AS
$$
SELECT to_timestamp(LEFT(ts, 10)::int8) +
(
CASE
WHEN LENGTH(ts) = 13
THEN RIGHT(ts, 3) ELSE '0'
END||' ms'
)::interval
$$;