TimescaleDB / PostgreSQL daily maximums with timezone offset - postgresql

I have problem in sorting & querying data with timezone offset.
I have PostgreSQL + TimescaleDB where I have time series data. Target is to get daily maximums based on local time zone.
My data is in UTC but query needs to be in local timezone as my cumulative counters are resetting in local time.
Target is to have result as in below
id | time | dailymax | name
-------+---------------------+------------------+------------------------
10001 | 2020-01-05 | 0 | Property1
10002 | 2020-01-05 | 20930.3490579984 | Property2
10003 | 2020-01-05 | 28347.1322223556 | Property3
10001 | 2020-01-04 | 57872.3274949118 | Property1
10002 | 2020-01-04 | 101159.612050399 | Property2
10003 | 2020-01-04 | 34113.226521315 | Property3
10001 | 2020-01-03 | 0 | Property1
10002 | 2020-01-03 | 17386.2914203308 | Property2
10003 | 2020-01-03 | 160599.774657208 | Property3
My best guess has been so far query in below, but it gives me also records from wrong days and takes long to execute
SELECT table.id, CAST(datetime AS DATE) AT TIME ZONE 'America/New_York' as time, MAX(value) AS value, map.tagname FROM table
JOIN map ON table.id = map.id
WHERE table.id in(10003,10029,10008,10036,10007) AND datetime AT TIME ZONE 'UTC' >= '2019-12-30T16:09:32.080Z' AND datetime AT TIME ZONE 'UTC' < '2020-01-06T16:09:32.080Z'
GROUP BY table.id, map.tagname, time
ORDER BY time DESC;
With TimescaleDB tried something as in below with bad luck as well
SELECT table.id, to_char(time_bucket('24 hours', datetime) at time zone 'utc', 'YYYY-MM-DD"T"HH24:MI:SS"Z"') as time, max(value) as value, map.name FROM
table JOIN map ON table.id = map.id
WHERE table.id in(10001,10002) AND datetime >= '2020-01-01' AND datetime < '2020-01-10'
GROUP BY time, table.id, map.name ORDER BY time DESC`;

Related

Select the highest value of every N days

| Date | Price |
| 2022-05-11 04:00:00.0000000 +00:00 | 1 |
| 2022-05-12 04:00:00.0000000 +00:00 | 2 |
| 2022-05-13 04:00:00.0000000 +00:00 | 3 |
I have a long table which looks like above with various timestamps. I would like to select the highest price of every N days. How should I do the grouping?
Thanks #EdmCoff, In my case the answer looks like
select MAX(Price)
from MyTable
Group by DATEADD(DAY, 0, 3 * FLOOR(DATEDIFF(DAY, 0, Date) / 3) )
order by min(Date) asc```

Get specific time period from a list of epoch in Postgres

I have the following table with epoch timestamps in Postgres. I would like to select the timestamps where the time is from 20:00 to 21:00 in PST. I have tried the following partially but I can't seem to extract both hour and minutes.
SELECT timestamp from table where extract(‘hour’ from to_timestamp(created_at) at time zone ‘America/Los_angeles’) > 20
| created_at |
| 1526528788 |
| 1526442388 |
| 1526309188 |
| 1526359588 |
| 1526532388 |
| 1526489188 |
Expected result:
| created_at |
| 1526528788 |
| 1526442388 |
Any help would be greatly appreciated.
Why do you write America/Los Angeles when you mean PST? They are (sometimes) different.
Does that solve your problem:
... WHERE extract(hour FROM
to_timestamp(1526309188) AT TIME ZONE 'PST'
) BETWEEN 20 AND 21;

How to check if there's a record in every hour in specified time-frame and then count it?

I'm using PostgreSQL and this is my table measurement_archive:
+-----------+------------------------+------+-------+
| sensor_id | time | type | value |
+-----------+------------------------+------+-------+
| 123 | 2017-11-26 01:53:11+00 | PM25 | 34.32 |
+-----------+------------------------+------+-------+
| 123 | 2017-11-26 02:15:11+00 | PM25 | 32.1 |
+-----------+------------------------+------+-------+
| 123 | 2017-11-26 04:32:11+00 | PM25 | 75.3 |
+-----------+------------------------+------+-------+
I need a query that will take records from specified timeframe (eg. from 2017-01-01 00:00:00 to 2017-12-01 23:59:59) and then check if in every hour there is at least 1 record - if there is, then add 1 to result.
So, if I make that query from 2017-11-26 01:00:00 to 2017-11-26 04:59:59+00 for sensor_id == 123 on above table then the result should be 3.
select count(*)
from (
select date_trunc('hour', time) as time
from measurement_archive
where
time >= '2017-11-26 01:00:00' and time < '2017-11-26 05:00:00'
and
sensor_id = 123
group by 1
) s
alternative solution would be using distinct,
select count(*) from (select distinct a, extract(hour from time) from t where time >'2017-11-26 01:00:11' and time <'2017-11-26 05:00:00' and sensor_id=123)t;

How to query just the last record of every second within a period of time in postgres

I have a table with hundreds of millions of records in 'prices' table with only four columns: uid, price, unit, dt. dt is a datetime in standard format like '2017-05-01 00:00:00.585'.
I can quite easily to select a period using
SELECT uid, price, unit from prices
WHERE dt > '2017-05-01 00:00:00.000'
AND dt < '2017-05-01 02:59:59.999'
What I can't understand how to select price for every last record in each second. (I also need a very first one of each second too, but I guess it will be a similar separate query). There are some similar example (here), but they did not work for me when I try to adapt them to my needs generating errors.
Could some please help me to crack this nut?
Let say that there is a table which has been generated with a help of this command:
CREATE TABLE test AS
SELECT timestamp '2017-09-16 20:00:00' + x * interval '0.1' second As my_timestamp
from generate_series(0,100) x
This table contains an increasing series of timestamps, each timestamp differs by 100 milliseconds (0.1 second) from neighbors, so that there are 10 records within each second.
| my_timestamp |
|------------------------|
| 2017-09-16T20:00:00Z |
| 2017-09-16T20:00:00.1Z |
| 2017-09-16T20:00:00.2Z |
| 2017-09-16T20:00:00.3Z |
| 2017-09-16T20:00:00.4Z |
| 2017-09-16T20:00:00.5Z |
| 2017-09-16T20:00:00.6Z |
| 2017-09-16T20:00:00.7Z |
| 2017-09-16T20:00:00.8Z |
| 2017-09-16T20:00:00.9Z |
| 2017-09-16T20:00:01Z |
| 2017-09-16T20:00:01.1Z |
| 2017-09-16T20:00:01.2Z |
| 2017-09-16T20:00:01.3Z |
.......
The below query determines and prints the first and the last timestamp within each second:
SELECT my_timestamp,
CASE
WHEN rn1 = 1 THEN 'First'
WHEN rn2 = 1 THEN 'Last'
ELSE 'Somwhere in the middle'
END as Which_row_within_a_second
FROM (
select *,
row_number() over( partition by date_trunc('second', my_timestamp)
order by my_timestamp
) rn1,
row_number() over( partition by date_trunc('second', my_timestamp)
order by my_timestamp DESC
) rn2
from test
) xx
WHERE 1 IN (rn1, rn2 )
ORDER BY my_timestamp
;
| my_timestamp | which_row_within_a_second |
|------------------------|---------------------------|
| 2017-09-16T20:00:00Z | First |
| 2017-09-16T20:00:00.9Z | Last |
| 2017-09-16T20:00:01Z | First |
| 2017-09-16T20:00:01.9Z | Last |
| 2017-09-16T20:00:02Z | First |
| 2017-09-16T20:00:02.9Z | Last |
| 2017-09-16T20:00:03Z | First |
| 2017-09-16T20:00:03.9Z | Last |
| 2017-09-16T20:00:04Z | First |
| 2017-09-16T20:00:04.9Z | Last |
| 2017-09-16T20:00:05Z | First |
| 2017-09-16T20:00:05.9Z | Last |
A working demo you can find here

Order by created_date if less than 1 month old, else sort by updated_date

SQL Fiddle: http://sqlfiddle.com/#!15/1da00/5
I have a table that looks something like this:
products
+-----------+-------+--------------+--------------+
| name | price | created_date | updated_date |
+-----------+-------+--------------+--------------+
| chair | 50 | 10/12/2016 | 1/4/2017 |
| desk | 100 | 11/4/2016 | 12/27/2016 |
| TV | 500 | 12/1/2016 | 1/2/2017 |
| computer | 1000 | 12/28/2016 | 1/1/2017 |
| microwave | 100 | 1/3/2017 | 1/4/2017 |
| toaster | 20 | 1/9/2017 | 1/9/2017 |
+-----------+-------+--------------+--------------+
I want to order this table in a way where if the product was created less than 30 days those results should show first (and be ordered by the updated date). If the product was created 30 or more days ago I want it to show after (and have it ordered by updated date within that group)
This is what the result should look like:
products - desired results
+-----------+-------+--------------+--------------+
| name | price | created_date | updated_date |
+-----------+-------+--------------+--------------+
| toaster | 20 | 1/9/2017 | 1/9/2017 |
| microwave | 100 | 1/3/2017 | 1/4/2017 |
| computer | 1000 | 12/28/2016 | 1/1/2017 |
| chair | 50 | 10/12/2016 | 1/4/2017 |
| TV | 500 | 12/1/2016 | 1/2/2017 |
| desk | 100 | 11/4/2016 | 12/27/2016 |
+-----------+-------+--------------+--------------+
I've started writing this query:
SELECT *,
CASE
WHEN created_date > NOW() - INTERVAL '30 days' THEN 0
ELSE 1
END AS order_index
FROM products
ORDER BY order_index, created_date DESC
but that only bring the rows with created_date less thatn 30 days to the top, and then ordered by created_date. I want to also sort the rows where order_index = 1 by updated_date
Unfortunately in version 9.3 only positional column numbers or expressions involving table columns can be used in order by so order_index is not available to case at all and its position is not well defined because it comes after * in the column list.
This will work.
order by
created_date <= ( current_date - 30 ) , case
when created_date > ( current_date - 30 ) then created_date
else updated_date end desc
Alternatively a common table expression can be used to wrap the result and then that can be ordered by any column.
WITH q AS(
SELECT *,
CASE
WHEN created_date > NOW() - INTERVAL '30 days' THEN 0
ELSE 1
END AS order_index
FROM products
)
SELECT * FROM q
ORDER BY
order_index ,
CASE order_index
WHEN 0 THEN created_date
WHEN 1 THEN updated_date
END DESC;
A third approach is to exploit nulls.
order by
case
when created_date > ( current_date - 30 ) then created_date
end desc nulls last,
updated_date desc;
This approach can be useful when the ordering columns are of different types.