Different results when subtracting two timestamps using AGE (PostgreSQL) - sql

Can anyone help me to understand why these are showing different outputs?
SELECT
EXTRACT (epoch FROM ('2021-02-01 00:00:00'::timestamp - '2021-01-01 00:00:00'::timestamp)) / 3600 / 24 as time1,
EXTRACT (epoch FROM age('2021-02-01 00:00:00'::timestamp , '2021-01-01 00:00:00'::timestamp)) / 3600 / 24 as time2
output:
time1
time2
31
30

There is a difference between the subtract operator and the function age(), per the documentation::
timestamp - timestamp › interval
Subtract timestamps (converting 24-hour intervals into days, similarly to justify_hours())
versus
age ( timestamp, timestamp ) › interval
Subtract arguments, producing a “symbolic” result that uses years and months, rather than just days
Example:
SELECT
'2021-02-01 00:00:00'::timestamp - '2021-01-01 00:00:00'::timestamp as interval1,
age('2021-02-01 00:00:00'::timestamp , '2021-01-01 00:00:00'::timestamp) as interval2
interval1 | interval2
-----------+-----------
31 days | 1 mon
(1 row)
The second interval is converted to 30 days in the further calculations described in the question.

Related

How to simplify the search with an interval in an SQl query?

Please tell me if it is somehow easier to write this query where the check occurs at intervals?
SELECT type, time_from, time_to
FROM manager_orders
WHERE aptid = 262707
AND cancelled_at is null
AND ('2021-04-26 11:00:00'
BETWEEN time_from - INTERVAL 30 Minute
AND time_to + INTERVAL 30 Minute
OR '2021-04-26 11:00:00'
BETWEEN time_from - INTERVAL 30 Minute
AND time_to + INTERVAL 60 Minute
)

get time series in 10 minutes of interval

I am generating one time-series from using below query.
SELECT date_trunc('min', dd):: TIMESTAMP WITHOUT TIME zone as time_ent
FROM generate_series ( timestamp '2021-12-09 06:34:37' + ((DATE_PART('min', timestamp '2021-12-09 06:34:37')::integer % 2) || ' minutes') :: INTERVAL
, '2021-12-10 06:34:37'::timestamp
, '20 min'::interval) dd
and it will give me output like below.
2021-12-09 06:34:00.000
2021-12-09 06:54:00.000
2021-12-09 07:14:00.000
2021-12-09 07:34:00.000
but I need output like.
2021-12-09 06:40:00.000
2021-12-09 07:00:00.000
2021-12-09 07:20:00.000
2021-12-09 07:40:00.000
currently, the time series hours depend upon the timestamp that I pass. in above it gives me mins like 34,54,14...but I want the mins like 40,00,20...it should not depend on the time I passed in query. I tried with timestamp '2021-12-09 06:34:37' + ((DATE_PART('min', timestamp '2021-12-09 06:34:37')::integer % 2) || ' minutes') :: INTERVAL but not any success.
Based on your description, I assumed that you want to create a series of timestamps for 00, 20, 40 minute at each hour until the next day.
select *
from (
select date_trunc('hour', current_timestamp) + i * interval '20 minutes' as ts
from generate_series(1, 24*3) as t(i)) t
where ts between current_timestamp and current_timestamp + interval '1 day'
The key idea here is to truncate the current_timestamp to 00 minute first. This becomes the start of the series. Then filter out the generated timestamps outside the range you want. You may need to adjust the second argument of generate_series function, depending on your requirement.
Or you can just generate a series of timestamp like the following:
select *
from (
select ts
from generate_series(
date_trunc('hour', current_timestamp),
current_timestamp + interval '1 day',
interval '20 minutes') as t(ts)) as t
where ts between current_timestamp and current_timestamp + interval '1 day'
Here, you still need to trunc your timestamp to hour first so the start of the series at 00 minute.

Get elapsed/covered time in PostgreSQL

I have the following table:
CREATE TABLE my_table
(
the_visitor_id varchar(5) NOT NULL,
the_visitor_visit timestamp NOT NULL
)
INSERT INTO my_table
VALUES ('VIS01', '2019-05-02 09:00:00'),
('VIS02', '2019-05-04 12:00:00'),
('VIS03', '2019-05-06 18:30:00'),
('VIS04', '2019-05-15 12:00:00'),
('VIS05', '2019-06-30 18:00:00')
I want to retrieve the time elapsed/covered (from a reference point) with decimals by hour, day and week. For example, for VIS01, the time elapsed would be 0.375 (9/24 from 0:00:00), the day elapsed would be 0.04435 (1 day 9 hours makes 1.375 divided by 31 days is 0.04435 in May, the month of the visit) and the week elapsed would be 3.375 (3 days 0.375 hours from the Monday in the week).
For VIS05 the time elapsed is 0.75 (18 hours/24 hours), the day elapsed would be 0.9917 (29 days .75 covered hours makes 29.75 divided by 30 days of June) and the week elapsed would be 6.75 (6 days from Monday in a week that starts on Monday and finish on Sunday plus 0.75 hours)
This should be the result:
the_visitor_id visitor_time_el visitor_days_el visitor_week_el
VIS01 0.375 0.0444 3.375
VIS02 0.5 0.1129 5.5
VIS03 0.7708 0.1539 0.7708
VIS04 0.5 0.4677 2.5
VIS05 0.75 0.9917 6.75
I've been stuck because I want to use this clause:
SELECT the_visitor_visit - date_trunc('month', the_visitor_visit) as visitor_days_el
But I don't quite get how I can convert that to decimals, and what to do with visitor_week_el. Please, any help will be greatly appreciated.
This is basically using epoch to extract the seconds from various date differences:
select t.*,
extract(epoch from the_visitor_visit::time) / (60 * 60 * 24) as visitor_time_el,
(extract(epoch from (the_visitor_visit - date_trunc('month', the_visitor_visit))) /
extract(epoch from (date_trunc('month', the_visitor_visit) + interval '1 month' - date_trunc('month', the_visitor_visit)))
) as visitor_day_el,
extract(epoch from (the_visitor_visit - date_trunc('week', the_visitor_visit))) / (60 * 60 * 24) as visitor_week_el
from my_table t
Here is a db<>fiddle.

Combine Output of Queries in Pivot Table with PostgreSQL

Suppose I have a table called orders that looks like this:
id
order date
Orders_Wanted
Orders_Given
1
2020-11-29 19:12:44.417
2
6
1
2020-11-29 20:12:44.417
2
6
1
2020-11-30 23:37:28.692
8
2
1
2020-11-30 23:37:28.692
2
6
How do I write a query that shows the count of orders_wanted - orders_given by hour broken down into two columns, one that counts positive results and one that counts negative results (a note that orders_wanted and orders_given are times, so that's why I am calculating orders_wanted - orders_given). I would also like to add a final column that calculates the percentage of total orders per hour that are positive (count_orders_positive/ (count_orders_negative + Count_orders_positive)).
The output of the query would look something like this:
week
day
hour
count_orders_positive
count_orders_negative
Percentage_orders_positive
48
7
19
0
1
100%
48
7
20
0
1
100%
49
1
23
1
1
50%
So far I am able to get the bottom two results using these queries, but I don't know how to combine them.
SELECT
extract (week from (order_date at time zone 'MST' at time zone 'UTC') ) as "week",
extract (isodow from (order_date at time zone 'MST' at time zone 'UTC') ) as "day",
extract (hour from (order_date at time zone 'MST' at time zone 'UTC') ) as "hour",
Count (extract (hour from (order_date at time zone 'MST' at time zone 'UTC') )) as
"count_orders_positive"
from orders
WHERE orders_wanted - orders_given >= 0
group by week, day, hour
order by week, day, hour;
week
day
hour
count_orders_positive
49
1
23
1
SELECT
extract (week from (order_date at time zone 'MST' at time zone 'UTC') ) as "week",
extract (isodow from (order_date at time zone 'MST' at time zone 'UTC') ) as "day",
extract (hour from (order_date at time zone 'MST' at time zone 'UTC') ) as "hour",
Count (extract (hour from (order_date at time zone 'MST' at time zone 'UTC') )) as
"count_orders_negative"
from orders
WHERE orders_wanted - orders_given < 0
group by week, day, hour
order by week, day, hour;
week
day
hour
count_orders_negative
48
7
19
1
48
7
20
1
49
1
23
1
You can do conditional aggregation. avg() comes handy to compute the percentage:
select
extract (week from (order_date at time zone 'MST' at time zone 'UTC') ) as "week",
extract (isodow from (order_date at time zone 'MST' at time zone 'UTC') ) as "day",
extract (hour from (order_date at time zone 'MST' at time zone 'UTC') ) as "hour",
count(*) filter (where orders_wanted - orders_given >= 0) as count_orders_positive,
count(*) filter (where orders_wanted - orders_given < 0) as count_orders_negative,
100 * avg((orders_wanted - orders_given >= 0)::int) as percent_orders_positive
from orders
group by week, day, hour
order by week, day, hour;

BigQuery: extract SECOND from TIMESTAMP

How can i run this query?
Error Message: No matching signature for function EXTRACT for argument types: DATE_TIME_PART FROM INT64. Supported signatures: EXTRACT(DATE_TIME_PART FROM DATE); EXTRACT(DATE_TIME_PART FROM TIMESTAMP [AT TIME ZONE STRING]); EXTRACT(DATE_TIME_PART FROM DATETIME); EXTRACT(DATE_TIME_PART FROM TIME) at [12:12]
They both give the same error message
WHERE EXTRACT( SECOND FROM event_timestamp )
- EXTRACT( SECOND FROM last_event) >= (60 * 10)
OR last_event IS NULL
WHERE EXTRACT( SECOND FROM event_timestamp AT TIME ZONE "UTC")
- EXTRACT( SECOND FROM last_event AT TIME ZONE "UTC") >= (60 * 10)
OR last_event IS NULL
use TIMESTAMP_MICROS()
WHERE EXTRACT( SECOND FROM TIMESTAMP_MICROS(event_timestamp))
- EXTRACT( SECOND FROM last_event) >= (60 * 10)
OR last_event IS NULL
If you want events that are more than 10 minutes from the previous timestamp, just use some arithmetic and comparisons:
where event_timestamp > last_event + (60 * 10 * 1000000) or
last_event is null
You are storing the timestamp as a microseconds value. You don't need to convert to another type.
If you really wanted to convert this to timestamp values, you could use:
where timestamp_micros(event_timestamp) > timestamp_add(timestamp_micros(last_event), interval 10 minute) or
last_event is null
In particular, you don't want to extract seconds. That value is always going to be between 0 and 59.