Difference between two timestamps as timestamp across multiple days - postgresql

I have two timestamps and I would like to have a result with the difference between them. I found a similar question asked here but I have noticed that:
select
to_char(column1::timestamp - column2::timestamp, 'HH:MS:SS')
from
table
Gives me an incorrect return if these timestamps cross multiple days. I know that I can use EPOCH to work out the number of hours/days/minutes/seconds etc but my use case requires the result as a timestamp (or a string...anything not an interval!).
In the case of multiple days I would like to continue counting the hours, even if it should go past 24. This would allow results like:
36:55:01

I'd use the built-in date_part function (as previously described in an older thread: How to convert an interval like "1 day 01:30:00" into "25:30:00"?) but finally cast the result to the type you desire:
SELECT
from_date,
to_date,
to_date - from_date as date_diff_interval,
(date_part('epoch', to_date - from_date) * INTERVAL '1 second')::text as date_diff_text
from (
(select
'2018-01-01 04:03:06'::timestamp as from_date,
'2018-01-02 16:58:07'::timestamp as to_date)
) as dates;
This results in the following:
I'm currently unaware of any way to convert this interval into a timestamp and also not sure whether there is a use for it. You're still dealing with an interval and you'd need a point of reference in time to transform that interval into an actual timestamp.

Related

Get truncked data from a table - postgresSQL

I want to get truncked data over the last month. My time is in unix timestamps and I need to get data from last 30 days for each specific day.
The data is in the following form:
{
"id":"648637",
"exchange_name":"BYBIT",
"exchange_icon_url":"https://cryptowisdom.com.au/wp-content/uploads/2022/01/Bybit-colored-logo.png",
"trade_time":"1675262081986",
"price_in_quote_asset":23057.5,
"price_in_usd":1,
"trade_value":60180.075,
"base_asset_icon":"https://assets.coingecko.com/coins/images/1/large/bitcoin.png?1547033579",
"qty":2.61,
"quoteqty":60180.075,
"is_buyer_maker":true,
"pair":"BTCUSDT",
"base_asset_trade":"BTC",
"quote_asset_trade":"USDT"
}
I need to truncate data based on trade_time
How do I write the query?
The secret sauce is the date_trunc function, which takes a timestamp with time zone and truncates it to a specific precision (hour, day, week, etc). You can then group based on this value.
In your case we need to convert these unix timestamps javascript style timestamps to timestamp with time zone first, which we can do with to_timestamp, but it's still a fairly simple query.
SELECT
date_trunc('day', to_timestamp(trade_time / 1000.0)),
COUNT(1)
FROM pings_raw
GROUP BY date_trunc('day', to_timestamp(trade_time / 1000.0))
Another approach would be to leave everything as numbers, which might be marginally faster, though I find it less readable
SELECT
(trade_time/(1000*60*60*24))::int * (1000*60*60*24),
COUNT(1)
FROM pings_raw
GROUP BY (trade_time/(1000*60*60*24))::int

Subract Additional Time from $__timeFilter

I want to subract additional time in $__timeFilter in grafana. Like if I have selected Last 7 days, I want to run 2 queries which do a comparison like one query gives me avg cpu utilization for last 7 days and another one gives me avg cpu utilzation for now() - 14d to now() - 7d. And this is dynamic. I can get for 6hrs, 2days or anything selected.
My database is TimescaleDB and grafana version in 8.3.5
Edit
Query is
select avg(cpu) from cpu_utilization where $__timeFilter(timestamp)
Whatever is selected in the time filter in grafana, the query is manipulated accordingly
Now with grafana understands this query becomes as following. if I select last 24hrs
select avg(cpu) from cpu_utilization where timestamp BETWEEN '2022-09-07 05:32:10' and '2022-09-08 05:32:10'
This is normal behaviour. Now I wanted that if I select last 24hrs, this query to behave as it is but an additional query becomes
select avg(cpu) from cpu_utilization where timestamp BETWEEN '2022-09-06 05:32:10' and '2022-09-07 05:32:10'
(I just don't want it for last 24hrs, but any relative time period selected in the filter)
Answer : https://stackoverflow.com/a/73658919/14817486
You can use the global variables $__to and $__from.
For example, ${__from:date:seconds} will give you a timestamp in seconds. You can then subtract 7 days (= 604800 seconds) from it and use it in your query's WHERE clause. Depending on your SQL dialect, that might be by using TIMESTAMP(), TO_TIMESTAMP() or something similar. So it would look similar to this:
[...] WHERE timestamp BETWEEN TO_TIMESTAMP(${__from:date:seconds}-604800) AND TO_TIMESTAMP(${__to:date:seconds}-604800) [...]
Interesting question! If I understood correctly, you could use the timestamp column as the reference as the grafana is already filtering by this to the comparison query. So you can get the min(timestamp) and max(timestamp) to know the limits of your period and then build something from it.
Like min(timestamp) - INTERVAL '7 days' would give you the start of the previous range, and max(timestamp) - INTERVAL '7 days' would offer the final scope.

Subtract between timestamp in Redshift

I found a weird thing. If a timestamp value subtract another, then Redshift will return an strange prefix. For example,
select table1.c_timestamp - table1.c_timestamp from table_1
Expect result should be ZERO or similar something, because these two timestamp values are same.
However, what I received is "5012369 years 4 mons", which I have no idea how does Redshift calculate the result.
Is there anyone can show me some clues?
Thanks
Contrary to the other answer,
Datediff doesn't exactly subtract, but rather counts the number of times the datepart chosen starts between the two timestamps.
datediff(second, '2018-04-10 00:00:00.001','2018-04-10 00:00:00.999')
>> 0
select datediff(second, '2018-04-10 00:00:00.999','2018-04-10 00:00:01.001')
>> 1
See: Datediff documentation
Edit: this is the way I found of how to perform the OP's task
SELECT
round(((EXTRACT('epoch' FROM TIMESTAMP '2018-05-27 09:59:59.999') - EXTRACT('epoch' FROM TIMESTAMP '2018-05-27 09:59:59.001'))*1000 + EXTRACT(millisecond FROM TIMESTAMP '2018-05-27 09:59:59.999') - EXTRACT(millisecond FROM TIMESTAMP '2018-05-27 09:59:59.001'))::real/1000)
The right way to subtract between datetimes is:
select datediff(seconds, table1.c_timestamp, table1.c_timestamp) from table_1
Of course, it doesn't make much sense to subtract a timestamp from itself, because that obviously returns 0, but I assume you just run that as a test.

Using generate_series() to produce 30 day date ranges for each day?

My goal is to create a table that looks something like this using PostgreSQL:
date date_start date_end
12/16/2015 11/17/2015 12/16/2015
12/17/2015 11/18/2015 12/17/2015
etc.
So that I can then join to a different table to get the aggregations for each date on a rolling 30 day window. I've been doing some research and I think generate_series() is what I want to use, but I am unsure.
Something like this:
SELECT '2015-12-16'::date + g - 30 AS date_start
, '2015-12-16'::date + g AS date_end
FROM generate_series (0, 25) g; -- number of rows
I skipped the redundant date column. (You shouldn't use a basic type name as identifier anyways.)
There is a variant of generate_series() that works with timestamps, but the simple version generating integer numbers is just as good for dates. Maybe even better because you avoid possible confusion with time zones.
Always use the ISO 8601 format for date literals, which is unambiguous with any datestyle or locale settings.
Related:
Rolling sum / count / average over date interval

Query only records that fall within a date range with postgresSQL (Redshift)

I am trying to grab all records from today's date to 14 weeks prior. I also need that same week but a year ago. I have the following:
WHERE date(date) >= date(dateadd(week,-14, current_date))
OR date(date) >= date(dateadd(week,-52, current_date))
OR date(date) <= date(dateadd(week,-53, current_date))
It doesn't seem to be working properly.
You could look into the use of INTERVAL to subtract from GETDATE() -- it may make things easier to read/reason about, but DATEADD should work in theory (although it isn't standard Postgres -- INTERVAL is).
One thing that immediately stands out from your stated requirements versus your code is that as written, from a boolean-logic perspective, the code doesn't match your requirements.
You said you need all records from between today and 14 weeks ago, and also from the same week a year ago.
What your code says is: give me all records where the date is within the past 14 weeks, or the date is greater than 52 weeks ago or the date is less than 53 weeks ago.
From a purely boolean perspective, what you want is more like something like this:
WHERE date(date) >= date(dateadd(week,-14, current_date))
OR (date(date) >= date(dateadd(week,-52, current_date))
AND date(date) <= date(dateadd(week,-53, current_date))
)
Note the additional set of parenthesis, and the switch amongst the additional set from or to and.
Additionally, I think you also may want to, within that additional set of parens, reverse the >= and <=, since in this case it's the larger negative number that represents the earlier date. This will all depend on exactly how DATEADD works, which I'm not familiar with since I use Postgres instead of Redshift (Redshift is a fork of Postgres 8.0), but based on how the Redshift doc seems to say it works, I believe you do want them reversed.
So then it becomes:
WHERE date(date) >= date(dateadd(week,-14, current_date))
OR (date(date) <= date(dateadd(week,-52, current_date))
AND date(date) >= date(dateadd(week,-53, current_date))
)
This is what I meant by it might be easier to reason about the logic when using INTERVAL -- using these large negative offsets, while they can certainly be made to work, just aren't as intuitive to reason about, IMO. It may also be worth switching the two sides of the inner AND, so the -53, the earlier value, is on the left side (won't change the functionality, but may make the use of DATEADD easier to reason about within this code).
Amazon Redshift is based on PostgreSQL 8.0. It doesn't support the "interval" data type, but docs say it does support interval arithmetic. I don't see any hint that it supports dateadd(), but I might have missed it.
This query will work as written on current versions of PostgreSQL and on SQLFiddle for testing and verification. I don't think it will work as written on Redshift, but the important part for you is just the WHERE clause. The common table expression is just to give us a calendar to do interval arithmetic against. You won't need it on Redshift.
It's not clear what you mean by "that same week but a year ago". You don't mention any particular week; you seem to be interested in a 14-week period. This query returns rows from
2014-09-23 (14 weeks ago) to 2014-12-30 (current date), and
the same dates in 2013.
with calendar as (
select (generate_series((current_date - interval '18 months'), current_date, '1 day'))::date cal_date
)
select cal_date
from calendar
where cal_date between (current_date - interval '14 weeks')::date
and current_date
or cal_date between ((current_date - interval '1 year')::date - interval '14 weeks')::date
and (current_date - interval '1 year')::date
order by cal_date;
cal_date
--
2013-09-23
...
2013-12-30
2014-09-23
...
2014-12-30