Extracting the number of days from a calculated interval - postgresql

I am trying to get a query like the following one to work:
SELECT EXTRACT(DAY FROM INTERVAL to_date - from_date) FROM histories;
In the referenced table, to_date and from_date are of type timestamp without time zone. A regular query like
SELECT to_date - from_date FROM histories;
Gives me interval results such as '65 days 04:58:09.99'. But using this expression inside the first query gives me an error: invalid input syntax for type interval. I've tried various quotations and even nesting the query without luck. Can this be done?

SELECT EXTRACT(DAY FROM INTERVAL to_date - from_date) FROM histories;
This makes no sense. INTERVAL xxx is syntax for interval literals. So INTERVAL from_date is a syntax error, since from_date isn't a literal. If your code really looks more like INTERVAL '2012-02-01' then that's going to fail, because 2012-02-01 is not valid syntax for an INTERVAL.
The INTERVAL keyword here is just noise. I suspect you misunderstood an example from the documentation. Remove it and the expression will be fine.
I'm guessing you're trying to get the number of days between two dates represented as timestamp or timestamptz.
If so, either cast both to date:
SELECT to_date::date - from_date::date FROM histories;
or get the interval, then extract the day component:
SELECT extract(day from to_date - from_date) FROM histories;

This example demontrates the creation of a table with trigger which updates the difference between a stop_time and start_time in DDD HH24:MI:SS format where the DDD stands for the amount of dates ...
DROP TABLE IF EXISTS benchmarks ;
SELECT 'create the "benchmarks" table'
;
CREATE TABLE benchmarks (
guid UUID NOT NULL DEFAULT gen_random_uuid()
, id bigint UNIQUE NOT NULL DEFAULT cast (to_char(current_timestamp, 'YYMMDDHH12MISS') as bigint)
, git_hash char (8) NULL DEFAULT 'hash...'
, start_time timestamp NOT NULL DEFAULT DATE_TRUNC('second', NOW())
, stop_time timestamp NOT NULL DEFAULT DATE_TRUNC('second', NOW())
, diff_time varchar (20) NOT NULL DEFAULT 'HH:MI:SS'
, update_time timestamp DEFAULT DATE_TRUNC('second', NOW())
, CONSTRAINT pk_benchmarks_guid PRIMARY KEY (guid)
) WITH (
OIDS=FALSE
);
create unique index idx_uniq_benchmarks_id on benchmarks (id);
-- START trigger trg_benchmarks_upsrt_diff_time
-- hrt = human readable time
CREATE OR REPLACE FUNCTION fnc_benchmarks_upsrt_diff_time()
RETURNS TRIGGER
AS $$
BEGIN
-- NEW.diff_time = age(NEW.stop_time::timestamp-NEW.start_time::timestamp);
NEW.diff_time = to_char(NEW.stop_time-NEW.start_time, 'DDD HH24:MI:SS');
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_benchmarks_upsrt_diff_time
BEFORE INSERT OR UPDATE ON benchmarks
FOR EACH ROW EXECUTE PROCEDURE fnc_benchmarks_upsrt_diff_time();
--
-- STOP trigger trg_benchmarks_upsrt_diff_time

Just remove the keyword INTERVAL:
SELECT EXTRACT(DAY FROM to_date - from_date) FROM histories;

Related

postgresql filter users by age range

Is there a better way of doing this?
Basically, I have a users table, and on of the columns is birth_date (date)
I am supposed to filter by age range, meaning, I will get a range like 18-24.
This will be passed to a function in a jsonb parameter, as an array of 2 integers.
So I have done the following
create or replace function my_filter_function(
p_search_parameters jsonb
)
returns TABLE(
user_id bigint,
birth_date date,
age interval,
years double precision
)
security definer
language plpgsql
as
$$
begin
return query
select u.user_id, u.birth_date, age(u.birth_date), date_part('year', age(u.birth_date))
from users u
where u.birth_date is not null
and ( (p_search_parameters#>>'{age,0}') is null or u.birth_date <= (now() - ((p_search_parameters#>>'{age,0}')::integer * interval '1 year'))::date)
and ( (p_search_parameters#>>'{age,1}') is null or u.birth_date >= (now() - ((p_search_parameters#>>'{age,1}')::integer * interval '1 year'))::date)
;
end;
$$;
-- this is just a aluttle helpder function to better post and explain the question
This seems to be doing the job, but was hoping to find other ways of doing this while still getting a jsonb parameter, array of 2 integers
Any ideas?

Inserting into Remote Db via Dblink

I'm trying to insert into remote DB via this query
The query works fine When I only select these columns but when I'm trying to insert with a 30-day interval condition it shows that an error in syntax
I don't know What I'm doing wrong in the interval syntax for selecting data of 30 days.
Can anyone help me with this!
Thanks
INSERT INTO tc_new_events
SELECT * FROM dblink ('dev_connec','SELECT id,type,eventtime,
deviceid,positionid,geofenceid,
attributes,maintenanceid FROM tc_events
WHERE eventTime < (NOW() - '30 days' INTERVAL )')
AS DATA (id int ,type varchar , eventtime timestamp without time zone ,
deviceid integer,positionid integer,geofenceid integer ,
attributes varchar,maintenanceid integer);
ERROR: syntax error at or near "30"
LINE 13: ...anceid FROM tc_events WHERE eventTime < (NOW() - '30 days' I...
^
SQL state: 42601
Character: 994

Postgres on conflict

CREATE FUNCTION get_or_create_id(scheduleid integer,member_id character varying, user_id integer,role_id integer, _appointment_date timestamp without time zone,active boolean) RETURNS INT AS
$$
WITH get AS (
SELECT memid FROM member_appointment_details d WHERE appointment_date=_appointment_date
), new AS (
INSERT INTO member_appointment_details (schedule_id,memberid,userid,roleid,appointment_date,active)
VALUES (scheduleid,member_id,user_id,role_id,_appointment_date,active)
ON CONFLICT (appointment_date) DO Nothing
RETURNING memid
)
SELECT memid FROM get
UNION ALL
SELECT memid FROM new
$$
LANGUAGE sql;
table 1
appointment_details has columns :
id integer NOT NULL GENERATED ALWAYS AS IDENTITY
appointment_date timestamp without time zone
table 2
appointment_schedule
id integer NOT NULL GENERATED ALWAYS AS IDENTITY
time_duration integer,
now i want to use conflict for the appointment_date from tbl1 with given duration dynamically like 15 mins 20 mins etc
can i use join in this function ? and how to get my exact output?
currently im getting 10:33:00 like this if i give 10:34:00 it is accepting but i want 10:33:00 + 15 mins = 10:48:00 i.e it should accept time + given duration of the given time

Postgresql 8.4 update query syntax error in plpgsql function

I am using PostgreSQL 8.4 and creating a plpgsql function. In the body of this function I have a query to update records.
...
UPDATE device_syncfiles SET
state_code = 1, updated_at = NOW() at time zone 'UTC'
WHERE
((state_code = 2 AND EXTRACT(EPOCH FROM (NOW() at time zone 'UTC' - updated_at::timestamp without time zone)) > 3600) OR
(state_code = 3 AND EXTRACT(EPOCH FROM (NOW() at time zone 'UTC' - updated_at::timestamp without time zone)) > 3600));
...
When I load this function into database, a syntax error turns out
ERROR: syntax error at or near "$1"
LINE 1: UPDATE device_syncfiles SET $1 = 1, $2 = NOW() at time z...
^
QUERY: UPDATE device_syncfiles SET $1 = 1, $2 = NOW() at time zone 'UTC' WHERE (( $1 = 2 AND EXTRACT(EPOCH FROM (NOW() at time zone 'UTC' - $2 ::timestamp without time zone)) > $3 ) OR ( $1 = 3 AND EXTRACT(EPOCH FROM (NOW() at time zone 'UTC' - $2 ::timestamp without time zone)) > $4 ))
CONTEXT: SQL statement in PL/PgSQL function "syncfile_get" near line 19
I cannot find any problem with this query. What's wrong here?
UPDATE: (missing information)
Table: device_syncfiles
id PK integer auto inc
user_id integer FK
file_name character varying(255) NOT NULL,
state_code integer NOT NULL FK,
md5 character varying(255) NOT NULL,
msg character varying(255),
created_at timestamp without time zone,
updated_at timestamp without time zone
Function: syncfile_get()
CREATE OR REPLACE FUNCTION syncfile_get()
RETURNS TABLE(id integer, user_id integer, file_name character varying, state_code integer, md5 character varying, created_at timestamp without time zone, updated_at timestamp without time zone) AS
$BODY$
DECLARE
_device_syncfile_id integer;
_download_timeout integer;
_processing_timeout integer;
BEGIN
-- GET all timeout info
SELECT state_timeout INTO _download_timeout FROM device_syncfile_states
WHERE state_name = 'downloading';
SELECT state_timeout INTO _processing_timeout FROM device_syncfile_states
WHERE state_name = 'processing';
-- GET syncfile id
_device_syncfile_id = NULL;
-- Reset timed out file to idel state
UPDATE device_syncfiles SET
state_code = 1, updated_at = NOW() at time zone 'UTC'
WHERE
((state_code = 2 AND EXTRACT(EPOCH FROM (NOW() at time zone 'UTC' - updated_at::timestamp without time zone)) > _download_timeout) OR
(state_code = 3 AND EXTRACT(EPOCH FROM (NOW() at time zone 'UTC' - updated_at::timestamp without time zone)) > _processing_timeout));
-- GET the id of one idel/timed out file => result could be a integer or NULL
SELECT device_syncfiles.id INTO _device_syncfile_id FROM device_syncfiles
WHERE
device_syncfiles.state_code = 1 OR
(device_syncfiles.state_code = 2 AND EXTRACT(EPOCH FROM (NOW() at time zone 'UTC' - device_syncfiles.updated_at::timestamp without time zone)) > _download_timeout) OR
(device_syncfiles.state_code = 3 AND EXTRACT(EPOCH FROM (NOW() at time zone 'UTC' - device_syncfiles.updated_at::timestamp without time zone)) > _processing_timeout)
LIMIT 1;
-- WHEN NULL skip state update and return empty set of record
-- Otherwise return the set of record with the id found in last step
IF _device_syncfile_id IS NOT NULL THEN
PERFORM syncfile_update(_device_syncfile_id, 2, NULL);
END IF;
RETURN QUERY SELECT
device_syncfiles.id,
device_syncfiles.user_id ,
device_syncfiles.file_name ,
device_syncfiles.state_code ,
device_syncfiles.md5 ,
device_syncfiles.created_at ,
device_syncfiles.updated_at
FROM device_syncfiles WHERE device_syncfiles.id = _device_syncfile_id;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
Many problems.
0.
I am using Postgresql 8.4
Postgres 8.4 reached EOL in July 2014. Consider upgrading to a current version. Urgently.
1.
Your question does not disclose the complete function (at least header and footer) nor any table definition and some sample data to help us help you.
I have to make assumptions, and my educated guess is that you have a function parameter named state_code, which conflicts with the identical column name. In three places. Basics:
Postgresql - INSERT RETURNING INTO ambiguous column reference
How to return result of a SELECT inside a function in PostgreSQL?
You must be aware the all fields declared in a RETURNS TABLE clause are effectively OUT parameters as well. (As stated in the first sentence of the first link.) So your Q update confirmed my assumptions.
2.
Your error message reports the first of those instances here:
UPDATE device_syncfiles SET
state_code = 1 ...
That's a consequence of 0.. You are tripping over your long dead and forgotten version of Postgres, where the superficial syntax check at function creation time used to detect a naming conflict between target columns of UPDATE statements and function parameters. Which is silly and was later removed: those target columns cannot conflict with function parameters on principal.
Your error reproduced in Postgres 8.4: dbfiddle here
The same does not happen in Postgres 9.4: dbfiddle here
To fix, best rename the function parameter to avoid conflicts. Related:
Postgres function returning a row as JSON value
3.
There are two more instances:
WHERE
((state_code = 2 AND EXTRACT(EPOCH FROM (NOW() at time zone 'UTC' - updated_at::timestamp without time zone)) > 3600) OR
(state_code = 3 AND EXTRACT(EPOCH FROM (NOW() at time zone 'UTC' - updated_at::timestamp without time zone)) > 3600));
Would need a fix in any version. Postgres cannot tell whether to resolve to the function parameter or the table column. Best table-qualify all columns to avoid any possible conflicts with parameter names a priori. (Except for UPDATE target columns, which do not need nor allow table qualification.)
4.
That's still lipstick on a pig. Improve the query like this:
UPDATE device_syncfiles d
SET state_code = 1
, updated_at = NOW() AT TIME ZONE 'UTC'
WHERE d.state_code IN (2, 3)
AND d.updated_at < (now() - interval '1 hour') AT TIME ZONE 'UTC';
Shorter, faster, can use an index on updated_at.
5.
Finally consider using timestamp with time zone instead of timestamp without time zone to begin with:
Ignoring timezones altogether in Rails and PostgreSQL

How to set default date on a field of a Postgres table?

How to add default date on postgresql?
I tried getdate() but the command gives a error:
ERROR: function getdate() does not exist
SQL state: 42883
Hint: No function matches the given name and argument types. You might need to add explicit type casts.
The command issued was:
Create TABLE CREATE_ORDERS (
Plot_ID VARCHAR (50) ,
USER_ID VARCHAR(50),
ORDER_ID VARCHAR(50),
TOTAL_AMOUNT float NOT NULL DEFAULT 0.000,
DOWNPAYMENT float NOT NULL DEFAULT 25000,
BALANCE_INSTALLMENT_AMOUNT float NOT NULL DEFAULT 0.000,
NO_OF_INSTALLMENTS float NOT NULL DEFAULT 12,
PURCHASE_DATE TIMESTAMP NOT NULL DEFAULT GETDATE(),
DUE_DATE TIMESTAMP NOT NULL DEFAULT GETDATE(),
CONSTRAINT AK_TransactionID1 UNIQUE(PLOT_ID),
CONSTRAINT AK_TransactionID2 UNIQUE(ORDER_ID),
PRIMARY KEY (PLOT_ID, ORDER_ID));
Use keyword CURRENT_TIMESTAMP instead of function GETDATE().
--Current date and time
SELECT CURRENT_TIMESTAMP;
--Current date
SELECT CURRENT_DATE;
--Current date and time
SELECT NOW();
More info here.
current_date function returns current date.
Check https://www.postgresql.org/docs/current/static/functions-datetime.html and https://www.postgresql.org/docs/current/static/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT for more details.