Select one record per minute group in postgres - postgresql

I have a big table in a postgres db with location of units. Now I need to retrieve a location for every 60 seconds.
In Mysql, this is a piece of cake: select * from location_table where unit_id = '123' GROUP BY round(timestamp / 60)
But in postgres this seems to be a very hard problem. I also have the timestamps in dateformat rather than in epoch format.
Here is an example of how the table looks
CREATE TABLE location_table (
unit_id int,
"timestamp" timestamp(3) without time zone NOT NULL,
lat double precision,
lng double precision
);

Use date_trunc() to make sets per minute:
SELECT * -- most likely not what you want
FROM location_table
WHERE unit_id = 123 -- numbers don't need quotes '
GROUP BY date_trunc('minute', 'timestamp');
The * is of course wrong, but I don't know what you want to know about the GROUP so I can't come up with something better.
Edit:
When you need a random result from your table, DISTINCT ON () could do the job:
SELECT DISTINCT ON(date_trunc('minute', timestamp))
* -- your columns
FROM location_table;
There are other (standard SQL) solutions as well, like using row_number().

Related

postgresql group by datetime in join query

I have 2 tables in my postgresql timescaledb database (version 12.06) that I try to query through inner join.
Tables' structure:
CREATE TABLE currency(
id serial PRIMARY KEY,
symbol TEXT NOT NULL,
name TEXT NOT NULL,
quote_asset TEXT
);
CREATE TABLE currency_price (
currency_id integer NOT NULL,
dt timestamp WITHOUT time ZONE NOT NULL,
open NUMERIC NOT NULL,
high NUMERIC NOT NULL,
low NUMERIC NOT NULL,
close NUMERIC,
volume NUMERIC NOT NULL,
PRIMARY KEY (
currency_id,
dt
),
CONSTRAINT fk_currency FOREIGN KEY (currency_id) REFERENCES currency(id)
);
The query I'm trying to make is:
SELECT currency_id AS id, symbol, MAX(close) AS close, DATE(dt) AS date
FROM currency_price
JOIN currency ON
currency.id = currency_price.currency_id
GROUP BY currency_id, symbol, date
LIMIT 100;
Basically, it returns all the rows that exist in currency_price table. I know that postgres doesn't allow select columns without an aggregate function or including them in "group by" clause. So, if I don't include dt column in my select query, i receive expected results, but if I include it, the output shows rows of every single day of each currency while I only want to have the max value of every currency and filter them out based on various dates afterwards.
I'm very inexperienced with SQL in general.
Any suggestions to solve this would be very appreciated.
There are several ways to do it, easiest one comes to mind is using window functions.
select *
from (
SELECT currency_id,symbol,close,dt
,row_number() over(partition by currency_id,symbol
order by close desc,dt desc) as rr
FROM currency_price
JOIN currency ON currency.id = currency_price.currency_id
where dt::date = '2021-06-07'
)q1
where rr=1
General window functions:
https://www.postgresql.org/docs/9.5/functions-window.html
works also with standard aggregate functions like SUM,AVG,MAX,MIN and others.
Some examples: https://www.postgresqltutorial.com/postgresql-window-function/

How to query by date and time in postgresql

i have a table where my date/time is of form: 2020-03-10 22:54:08
This is a timestampped object. I tried the following query but didn't return any rows:
select ts from table1
where cast(ts as timestamp) = '2020-03-10 22:54:08'
returns nothing.
How do i query based on date and time in postgressql?
A timestamp has microsecond resolution, so you have to use same techiques as when testing floating point numbers: Round it or use only < and > for comparison.
To retrieve data from a database, you need to refer to SQL SELECT syntax. In your situation, the ts column is already a timestamp, so there is no need to use cast(). Bear in mind, however, that a timestamp type contains fractions of a second (i.e., 2020-03-10 22:54:03.xxx), so you would be better off using a comparison operator (>,<,>=,or <=)
You can retrieve all columns by using the * syntax:
select *
from my_table
where ts >= '2020-03-10 22:54:08';
tymestamp type by default contains also microseconds, so, now() which, for example, is 2020-03-11 01:56:27.593985 here obviously is not equal to 2020-03-11 01:56:27. If you do not want to have microseconds precision in your data then declare your field like ts timestamp(0) NOT NULL DEFAULT now() which means "0 decimal digits for microseconds":
select
current_timestamp::timestamp as ts,
current_timestamp::timestamp(2) as ts2,
current_timestamp::timestamp(0) as ts0;
ts | ts2 | ts0
---------------------------+------------------------+---------------------
2020-03-11 02:02:52.98298 | 2020-03-11 02:02:52.98 | 2020-03-11 02:02:53
Actually this worked.
select distinct(ts) from my_table where ts >= '2020-03-10 22:54:08' and ts <= '2020-03-10 22:54:09'
But this doesn't give me the whole rows
But then i tried this and this worked:
select ts from table1
where to_char(ts,'YYYY-MM-DD HH24:MI:SS') = '2020-03-10 22:54:08'

Redshift: milliseconds to timestamp

Let us say we have have two tables:
CREATE TABLE IF NOT EXISTS tech_time(
ms_since_epoch BIGINT
);
CREATE TABLE IF NOT EXISTS readable_time(
ts timestamp without time zone,
);
Let us say tech_time has data and we would like to populate readable_time.
So in Postgres you could use to_timestamp(double precision) and do something like
INSERT INTO readable_time(ts)
SELECT DISTINCT to_timestamp(ms_since_epoch::float / 1000) AS ts,
FROM tech_time;
No such function seems to exist in Amazon Redshift:
function to_timestamp(double precision) does not exist
My question is: how do I properly populate readable_time, while losing the least amount of precision?
We can try using DATEADD and add the ms_since_epoch to January 1, 1970:
INSERT INTO readable_time (ts)
SELECT DATEADD(ms, ms_since_epoch, 'epoch')
FROM tech_time;

Postgres: query for a specific date on a timestampz returns no data while I can see the data in my postgres client

I'm very new at postgres and I've been googling this for about 1h before posting here.
Hopefully, you can help me with this probably trivial issue.
I have a database LTC that was created with this schema:
CREATE TABLE LTC (
id SERIAL PRIMARY KEY,
time timestamptz,
side CHAR(4),
price REAL,
v REAL,
n INT
);
I want to query to get all the data for time='2017-08-12T03:58:26.563Z' (ISO string from javascript). I know there are over a hundred lines of data in the database for that time, i see it in my postgres client.
Here is the query I'm doing:
select * from LTC where time = '2017-08-12T03:58:26.563Z'::timestamptz
Why am I getting no results?
Edit:
Still unsure why it wasn't working, but I wrote a work-around that does:
In JavaScript:
var date = new Date('2017-08-12T03:58:26.563Z').toISOString(); // actual time passed as parameter in my function, hard-coded for the example
var reg = new RegExp("([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})\.[0-9]*Z","gmi");
var start = date.replace(reg, function(match,year,month,day,hour,min,sec,ms) {
return year+'-'+month+'-'+day+'T'+hour+':'+min+':00.000Z';
});
var end = date.replace(reg, function(match,year,month,day,hour,min,sec,ms) {
var min = parseInt(min)+1;
if (min<=9) {
min = '0'+min;
}
return year+'-'+month+'-'+day+'T'+hour+':'+min+':00.000Z';
});
var query = "select * from LTC where time >= '"+start+"'::timestamptz and time < '"+end+"'::timestamptz"; // This works
Try using date_trunc('second',timestamp) on both to ensure that numerical precision is not what is throwing you off. There may be additional decimal places that are not being shown by your client and throwing off the equality.
The other possible solution is giving a range (between x and y) to avoid the numerical equality issue.
It would be easier for us to help if you can get an exact copy of the data being represented (using pg_dump perhaps, if you are familiar) so that we can test with the data that you are using.
The final thing you may want to check is explicitly stating the time zones that you are referencing. I generally use timestamp without time zone to avoid this issue, but auto-setting time zones to different values may be throwing you off as well. A good way to test is by selecting the two values, something like
select *, l.time = p.ts as test
from LTC l, (select '2017-08-12T03:58:26.563Z'::timestamptz as ts) p
;
EDIT:
I have built a test to try to reproduce your behavior:
CREATE TABLE LTC (
id serial
, time timestamptz
);
INSERT INTO LTC (time)
values ('2017-08-12T03:58:26.56312345'::timestamptz)
returning *;
select *
from LTC
where time = '2017-08-12T03:58:26.563Z'::timestamptz
;
select *, l.time = p.ts as test
from LTC l, (select '2017-08-12T03:58:26.563Z'::timestamptz as ts) p
;
What I get here is actually:
1;"2017-08-12 03:58:26.563123-04";"2017-08-11 23:58:26.563-04";f
Hopefully you can see what is happening - the '2017-08-12T03:58:26.563Z'::timestamptz is being interpreted as a UTC time and then converted to my time zone (UTC-04), so what is being compared is actually a different date altogether! In the future, showing this type of equality side-by-side is a great way to test that you are executing what you think you are (especially with dates / times where auto-conversion happens often).

How toConvert minutes into hours in postgresql

I'm new in PostgreSQL I need to convert the minute's value in a column into hours and minutes format I have searched in various sources but failed to achive.Some one please help me to achieve this.
In mean while I try to use to_char() as follows :
UPDATE tablename SET col2 = TO_CHAR(((col1*60 ||`second`))::interval, ‘HH24:MI:SS’) where id = 145;
but I get the following error...
column "late_by" is of type timestamp with time zone but expression is of type text
LINE 2: UPDATE attendance SET late_by = TO_CHAR(((lateby*60 || 'seco...
^
HINT: You will need to rewrite or cast the expression.
It is unclear what type each column in your statement is.
But if it helps, you can perform maths on intervals
select interval '60 seconds' * 15;
or in your case, if "col1" is integer: interval '60 seconds' * col1;
your column late_by is of time timestamp, yet you want to update it with interval, not timestamp. If you want to save how much time somebody is late, better use interval, eg:
t=# create table w3 (t interval);
CREATE TABLE
t=# insert into w3 select (185||' seconds')::interval;
INSERT 0 1
t=# select * from w3;
t
----------
00:03:05
(1 row)
as you can see "conversion" to minutes done by postgres itself