How to write bigint (timestamp in milliseconds) value as timestamp in postgresql - postgresql

I'm trying to store in timestamp with timezone field my value. It is in milliseconds from 1970.
select TO_CHAR(TO_TIMESTAMP(1401432881230), 'DD/MM/YYYY HH24:MI:SS.MS')
Expected 30/5/2014 11:29:42 10:54:41.230, but get
22/08/46379 23:27:02.000

Unix timestamps measures time with seconds, and not milliseconds (almost everywhere, in PostgreSQL too).
Therefore you need to call
SELECT TO_TIMESTAMP(1401432881230 / 1000);
If you want to preserve milliseconds, call with double precision:
SELECT TO_TIMESTAMP(1401432881230::double precision / 1000);

This is how I convert ms to timestamp and keep ms instead seconds.The accepted answer will drop ms.
WITH ts AS (SELECT 1401432881230 AS ts)
SELECT to_timestamp(ts / 1000) + ((ts % 1000 ) || ' milliseconds') :: INTERVAL
FROM ts;
-- FOR ALTER COLUMN
ALTER TABLE my_info
ALTER COLUMN tstmp TYPE TIMESTAMP USING to_timestamp(tstmp / 1000) + ((tstmp % 1000) || ' milliseconds') :: INTERVAL;

Okay, I understood. My INSERT should looks like:
INSERT INTO events (timestamp) VALUES (to_timestamp(TO_CHAR(TO_TIMESTAMP(1401432881222 / 1000), 'YYYY-MM-DD HH24:MI:SS') || '.' || (1401432881222%1000), 'YYYY-MM-DD HH24:MI:SS.MS'))
I'm converting bigint-timestamp with milliseconds to text with required format ('YYYY-MM-DD HH24:MI:SS.MS') and pass it to to_timestamp function.

Related

PostgreSQL query for elapsed interval

I am trying to query PostgreSQL database for rows where interval has elapsed from the last run. Main columns for this purpose are processed_at as timestamptz and frequency (in minutes) as integer.
I am failing with operators, since not many of them can operate together timestamp & integer.
Can someone please propose a query that would solve this? Thank you very much for help
From here Date/time operators:
timestamp + interval → timestamp
Add an interval to a timestamp
timestamp '2001-09-28 01:00' + interval '23 hours' → 2001-09-29 00:00:00
select now() + (10::varchar || ' min')::interval;
?column?
-------------------------------
2021-10-15 09:05:37.927163-07
--Or in your case. If I'm following you are adding the interval.
select processed_at + (frequency::varchar || ' min')::interval;
The query takes the integer value of minutes and converts it to an interval of minutes that can be added to the timestamp.
Further explanation, || is the Postgres concatenation operator and ::varchar, ::interval are casting shorthand.
UPDATE
I keep forgetting about the make_*() functions for date/time/interval
--A shorter version
select processed_at + make_interval(mins => frequency);
Saves all the casting.

How to handle timestamp with timezone on postgres with knex.js

I am trying to translate following sql query into knex:
select count(*) as finished_on_time from task_history
where date = 20160303
and store_id = 2
and (schedule_start_time at time zone 'Australia/sydney' + interval '1' minute * floor (group_duration) )::time >= (finish_time at time zone 'Australia/sydney')::time
date field has in yyyymmdd format
Here is what I have been trying on knex:
db.table('task_history')
.count('*')
.where({date: request.params.storeid, store_id: request.params.storeid })
??????
As you can guess, I am not sure which clause to use to handle sql syntax [at time zone Australia/sydney].
I have been trying to find any similar soloutions on the internet, but ended up here.
http://knexjs.org/#Builder-whereRaw
db.table('task_history')
.count('*')
.where({date: request.params.storeid, store_id: request.params.storeid })
.whereRaw("(schedule_start_time at time zone 'Australia/sydney' + interval '1' minute * floor (group_duration) )::time >= (finish_time at time zone 'Australia/sydney')::time")

SUM Data if Duplicate in Postgres

I am having table with three columns and may have duplicate data in it. what I am trying to do is if BATCH Column is having Duplicate Data then START_S and END_S should be according to below Example
CREATE TABLE "DRL_FTO3_DI1_A0_BATCH"
(
"BATCH" character varying(128),
"START_S" integer,
"END_S" integer
)
INSERT INTO "DRL_FTO3_DI1_A0_BATCH"(
"BATCH", "START_S", "END_S")
VALUES ('Batch 1_1',1451120920,1451121008),
('Batch 01_2',1451389014,1451389100),
('Batch 2_1',1451534680,1451534918),
('Batch 3_1',1451539145,1451539264),
('Parth_2',1451540990,1451541285),
('Parth_2',1451541676,1451542254)
SELECT "BATCH",((TIMESTAMP WITHOUT Time Zone 'epoch' + "START_S" * INTERVAL '1 second') AT TIME ZONE 'UTC')::TIMESTAMP WITHOUT Time Zone,
((TIMESTAMP WITHOUT Time Zone 'epoch' + "END_S" * INTERVAL '1 second') AT TIME ZONE 'UTC')::TIMESTAMP WITHOUT Time Zone
FROM "DRL_FTO3_DI1_A0_BATCH"
Now as we can see Parth_2 is duplicate value so START_S and END_S for Parth_S should be
Parth_2 2015-12-31 11:19:50 2015-12-31 11:40:54
You could do it using GROUP BY and MIN/MAX aggregate functions (you can convert into date time later with below query in format you desire) like:
SELECT BATCH, MIN(START_S), MAX(END_S)
FROM DRL_FTO3_DI1_A0_BATCH
GROUP BY BATCH

How to convert a column in seconds to dateformat in Teradata?

I have a column which is of bigint datatype(in seconds) which should be added to a date, so i need to convert this column into dateformat.
The arithmetic must be done against a timestamp data type in Teradata. The date data type does not have a time element associated with it. The following SQL should help point you in the right direction:
SELECT CAST(CAST(1234 AS BIGINT) AS INTERVAL SECOND(4)) AS Seconds_
, CURRENT_TIMESTAMP(0) AS CurrentTimestamp_
, CURRENT_TIMESTAMP + Seconds_ AS NewTimeStamp
If the number of seconds is less than 864000000 you can simply use interval arithmetic:
CAST(col AS TIMESTAMP) + (bigintcol * INTERVAL '0000 00:00:01' DAY TO SECOND)
Based on your other question your input is a Unixtime, those are two functions for converting them from/to Teradata timestamps:
/**********
Converting Unix/POSIX time to a Timestamp
Unix time: Number of seconds since 1970-01-01 00:00:00 UTC not counting leap seconds (currently 24 in 2011)
Also working for negative numbers.
The maximum range of Timestamps is based on the range of INTEGERs:
1901-12-13 20:45:52 (-2147483648) to 2038-01-19 03:14:07 (2147483647)
Can be changed to use BIGINT instead of INTEGER
20101211 initial version - Dieter Noeth
**********/
REPLACE FUNCTION UnixTime_to_TimeStamp (UnixTime INT)
RETURNS TimeStamp(0)
LANGUAGE SQL
CONTAINS SQL
DETERMINISTIC
SQL SECURITY DEFINER
COLLATION INVOKER
INLINE TYPE 1
RETURN
CAST(DATE '1970-01-01' + (UnixTime / 86400) AS TIMESTAMP(0))
+ ((UnixTime MOD 86400) * INTERVAL '00:00:01' HOUR TO SECOND)
;
SELECT
UnixTime_to_TimeStamp(-2147483648)
,UnixTime_to_TimeStamp(0)
,UnixTime_to_TimeStamp(2147483647)
;
/**********
Converting a Timestamp to Unix/POSIX time
Unix time: Number of seconds since 1970-01-01 00:00:00 UTC not counting leap seconds (currently 24 in 2011)
The maximum range of Timestamps is based on the range of INTEGERs:
1901-12-13 20:45:52 (-2147483648) to 2038-01-19 03:14:07 (2147483647)
Can be changed to use BIGINT instead of INTEGER
20101211 initial version - Dieter Noeth
**********/
REPLACE FUNCTION TimeStamp_to_UnixTime (ts TimeStamp(6))
RETURNS INTEGER
LANGUAGE SQL
CONTAINS SQL
DETERMINISTIC
SQL SECURITY DEFINER
COLLATION INVOKER
INLINE TYPE 1
RETURN
(CAST(ts AS DATE) - DATE '1970-01-01') * 86400
+ (EXTRACT(HOUR FROM ts) * 3600)
+ (EXTRACT(MINUTE FROM ts) * 60)
+ (EXTRACT(SECOND FROM ts))
;
SELECT
TimeStamp_to_UnixTime(TIMESTAMP '1901-12-13 20:45:52')
,TimeStamp_to_UnixTime(CURRENT_TIMESTAMP)
,TimeStamp_to_UnixTime(TIMESTAMP '2038-01-19 03:14:07')
;

Using a variable period in an interval in Postgres

I have a relation that maintains monthly historical data. This data is added to the table on the last day of each month. A service I am writing can then be called specifying a month and a number of months prior for which to retrieve the historical data. I am doing this by creating startDate and endDate variables, and then returning data between the two. The problem I am having is that startDate is a variable number of months before endDate, and I cannot figure out how to use a variable period in an interval.
Here is what I have:
DECLARE
endDate TIMESTAMP := (DATE_TRUNC('MONTH',$2) + INTERVAL '1 MONTH') - INTERVAL '1 DAY';
startDate TIMESTAMP := endDate - INTERVAL $3 'MONTH';
I know that the line for startDate is not correct. How is this properly done?
Use this line:
startDate TIMESTAMP := endDate - ($3 || ' MONTH')::INTERVAL;
and note the space before MONTH.
Basically: You construct a string with like 4 MONTH and cast it with ::type into a proper interval.
Edit: I' have found another solution: You can calculate with interval like this:
startDate TIMESTAMP := endDate - $3 * INTERVAL '1 MONTH';
This looks a little bit nicer to me.
This code has nothing directly to do with your situation, but it does illustrate how to use variables in INTERVAL arithmetic. My table's name is "calendar".
CREATE OR REPLACE FUNCTION test_param(num_months integer)
RETURNS SETOF calendar AS
$BODY$
select * from calendar
where cal_date <= '2008-12-31 00:00:00'
and cal_date > date '2008-12-31' - ($1 || ' month')::interval;
$BODY$
LANGUAGE sql VOLATILE
COST 100
ROWS 1000;
The most readable way I have found to pass a variable time period to Postgres is similar to A.H.'s answer: by multiplying by an integer. But this can be done without a cast.
Python example (with sqlalchemy and pandas):
import pandas as pd
import sqlalchemy as sa
connection = sa.create_engine(connection_string)
df = pd.read_sql(
sa.text('''
select * from events
where
event_date between now() - (interval '1 day' * :ndays) and now()
limit 100;
'''),
connection,
params={'ndays': 100}
)
The number of days (ndays) is passed as an integer from within Python - so unintended consequences are less likely.
My approach is like this.. It gives me option to set specific date or a relative range.
create or replace function search_data(_time_from timestamptz default null, _last_interval text default null)
returns setof journal
language plpgsql as
$$
begin
return query
select *
from journal
where created >= case
when _time_from is not null
then _time_from
else now() - _last_interval::interval end;
end;
$$;
While the above accepted answer is fine, it's a little bit antiquated - requiring a bit more mental energy to read than needed if you're running on Postgres 9.4+.
Old Way (Postgres Versions < 9.4)
startDate TIMESTAMP := endDate - $3 * INTERVAL '1 MONTH';
New Way (Postgres 9.4+)
startDate TIMESTAMP := endDate - MAKE_INTERVAL(MONTHS => $3);
If you are on Postgres 9.4+, the new MAKE_INTERVAL() function seems much more readable - probably why they created it.
If you want something you can run in your editor, here are a couple of
examples (I substituted the original variable binding $3 with the number 2 for an example of 2-months prior to the current date).
SELECT CURRENT_DATE - 2 * INTERVAL '1 MONTH';
SELECT CURRENT_DATE - MAKE_INTERVAL(MONTHS => 2);