In my program every table has a column last_modified:
last_modified int8 DEFAULT (date_part('epoch'::text, now()::timestamp) * (1000)::double precision) NOT NULL
For update I added a trigger:
CREATE OR REPLACE FUNCTION sync_lastmodified() RETURNS trigger AS $$
BEGIN
NEW.last_modified := (date_part('epoch'::text, now()::timestamp) * (1000)::double precision);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER
sync_lastmodified
BEFORE UPDATE ON
ourtable
FOR EACH ROW EXECUTE PROCEDURE
sync_lastmodified();
They should write current time as a long value into the last_modified column on update/insert.
However, it is not working as I expected.
To reproduce the issue, I did update and got the following:
last_modified value equals 1543576224455 (Friday November 30, 2018 16:10:24 (pm) in time zone Asia/Tashkent (+05))
Almost at the same time I run the function now from pgAdmin:
SELECT now()
and got the result:
2018-11-30 11:10:36.891426+05
To check system time within a few seconds I run timedatectl status from terminal and got the following result:
The question is why the function now() gives a time with 5 hours
difference when I run it from trigger or as a default value when
insert?
epoch will give you the number of seconds since the epoch. As the documentation says:
epoch
For timestamp with time zone values, the number of seconds since 1970-01-01 00:00:00 UTC (can be negative)
Since you are offset by 5 hours from UTC, that explains the difference.
Related
I wanted to calculate the elapsed time of a script in DB2 LUW .
I need to wright the code to get start time and end time then return the difference.
select current timestamp as startdate from sysibm.sysdummy1;
-- my querys
select current timestamp as enddate from sysibm.sysdummy1;
select timestampdiff (enddate , startdate);
Firstly, you are using timestampdiff() incorrectly -- please check the manual.
Secondly, you cannot select from nothing in Db2; you seem to know how to use sysibm.sysdummy1, so apply the same technique to your elapsed time calculation. Alternatively, you could use the values statement.
But worst of all, you don't save the result of your select current timestamp... queries anywhere, so you can't reference them later.
You could do something like this if you don't want to write SQL/PL code:
create table t (starttime timestamp, endtime timestamp);
-- you could also declare a global temporary table instead
insert into t (starttime) values (current timestamp);
-- your statements
update t set endtime=current timestamp;
select timestampdiff(1, char(endtime-starttime)) as elapsed_microseconds from t;
drop table t; -- if it's not a temp table
I want to see how long a loop iteration takes inside a DO block in postgres. The basic layout is as follows:
DO $$
declare v_time timestamptz;
declare i record;
begin
for i in select generate_series(1, 5) t
loop
select current_timestamp into v_time;
perform pg_sleep(i.t);
-- something done here (pg_sleep to ensure some time passes)
raise notice '%', v_time - (select current_timestamp);
-- expect negative interval from RAISE.
end loop;
end; $$;
However, when I run this (have tried on Postgres 13 and 9), I get an interval of 0S returned:
NOTICE: 00:00:00
NOTICE: 00:00:00
NOTICE: 00:00:00
NOTICE: 00:00:00
NOTICE: 00:00:00
DO
Query returned successfully in 15 secs 389 msec.
I have done this previously and have never run into this issue before, so I guess my question is "what am I doing wrong this time?" instead of "why is postgres behaving unexpectedly?"
current_timestamp is defined to be:
the start time of the current transaction, their values do not change during the transaction
you probably want to use clock_timestamp() instead which returns a value that changes within a transaction, see the link above for a more complete description.
I have ERP application that uses the system date when posting transactions. The database is PostgreSQL. I'm able to use https://www.nirsoft.net/utils/run_as_date.html for backdate the application but I notice that the transactions are still posting as of "today" and I think that maybe because of PostgreSQL using the system date.
Is there any way I can set the date back for PostgreSQL? Or any other way to do this? The process in the ERP application does not have an option to back date.
The easiest would be to add a trigger to the database that would change the date for inserted rows:
create table testpast(
id serial primary key,
time timestamp with time zone not null default now()
);
insert into testpast (time) values (default);
select * from testpast;
id | time
----+-------------------------------
1 | 2018-03-16 00:09:20.219419+01
(1 row)
create function time_20_years_back() returns trigger as $$
begin
NEW.time = now()-'20 years'::interval;
return NEW;
end;
$$ language plpgsql;
create trigger testpast_time_20_years_back
before insert on testpast
for each row
execute procedure time_20_years_back();
insert into testpast (time) values (default);
select * from testpast;
id | time
----+-------------------------------
1 | 2018-03-16 00:09:20.219419+01
2 | 1998-03-16 00:09:55.741345+01
(2 rows)
Though I have no idea what would be the purpose of such a hack.
This question already has answers here:
PostgreSQL - how to render date in different time zone?
(2 answers)
Closed 5 years ago.
I have a "timestamp with time zone" field in a table.
I need to return is as iso 8601 string in a give time zone.
The closest thing that I managed to do is this:
select to_char(crtdate, 'YYYY-MM-DD"T"HH24:MI:SS.MSOF:"00"') from t1
But it returns a timestamp in a system default time zone (UTC) i.e. something like this:
2017-07-12T02:46:26.194+00:00
while I need it be formatted for a specific time zone.
E.g.
2017-07-12T14:46:26.194+12:00
for "Pacific/Auckland".
Can someone please advise how this can be achieved?
we are on PG v 9.6.
Thank you,
You can play with GUC parameters datestyle and timezone inside a function to get what you want. Here is an example (however, it returns microseconds, so probably you'll need to tune it a bit):
create or replace function timestamp_iso8601(ts timestamptz, tz text) returns text as $$
declare
res text;
begin
set datestyle = 'ISO';
perform set_config('timezone', tz, true);
res := ts::timestamptz(3)::text;
reset datestyle;
reset timezone;
return replace(res, ' ', 'T') || ':00';
end;
$$ language plpgsql volatile;
Results:
test=# select timestamp_iso8601(now()::timestamptz, 'Europe/Moscow');
timestamp_iso8601
-------------------------------
2017-07-12T08:56:58.692985+03:00
test=# select timestamp_iso8601(now()::timestamptz, 'Pacific/Auckland');
timestamp_iso8601
-------------------------------
2017-07-12T17:59:05.863483+12:00
(1 row)
Update: edited. You can use timestamptz(3), specifying the precision (by default, it will go with microseconds, while 3 will keep only milliseconds). Alternatively, you can use res := to_char(ts::timestamptz, 'IYYY-MM-DDT HH24:MI:SS:MSOF'); instead of ::timestamptz(3)::text conversion chain, and in this case (3) will not be needed.
I need help with creating a trigger which forbids user to delete data that is newer than 2 weeks.
My current code:
CREATE OR REPLACE FUNCTION f_delete_data() RETURNS trigger AS $$
BEGIN
RAISE EXCEPTION 'Cant delete data which is newer than 2 weeks.';
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trig_delete_data BEFORE DELETE ON Results
FOR EACH ROW WHEN (OLD.Date < DATE_SUB(NOW(), INTERVAL 14 DAY)) EXECUTE PROCEDURE
f_delete_data();
This code says there's a syntax error at or near 14 ..
Why is the date_sub(..,interval 14 day) not working?
I'm using PostgreSQL 9.3.0.
Why isn't the date_sub(..,intercval 14 day) not working?
$$ LANGUAGE plpgsql; indicates you're using postgres however DATE_SUB is a MySQL specific function not available in postgres.
Try replacing DATE_SUB with this
(OLD.Date < NOW() - INTERVAL '14 DAYS')
Besides the obvious mistake (no DATE_SUB in Postgres), you also have your logic backwards. If you want to protect rows where the value in the date column is less than 2 weeks old: "newer than 2 weeks", then you must revert the comparison operator.
CREATE TRIGGER trig_delete_data
BEFORE DELETE ON results
FOR EACH ROW
WHEN (OLD.date < DATE_SUB(NOW(), INTERVAL 14 DAY))
WHEN (OLD.date < now() - interval '14 days')
WHEN (OLD.date > now() - interval '14 days')
EXECUTE PROCEDURE f_delete_data();
And f_delete_data() really should be name something like f_protect_new_data().
Or, if your columns is an actual date like the ill-chosen column name suggests, further simplify:
WHEN (OLD.date >= CURRENT_DATE - 14)
The manual on CURRENT_DATE & friends.
Use >= in this case, the 14th day back from today is still illegal according to your definition. The bound is logically a bit different from timestamp handling.
Why "ill-chosen"? "date" is a reserved word in standard SQL and a basic type name in Postgres. If the column actually holds a timestamp, not a date, it's misleading on top of that.