Postgtres: Calculate number of days as float considering hours/minutes - sql

I would like to compute the number of days since 1899/12/30, taking into consideration the number of hours and minutes.
So, for example data in my table:
+--------------------+-------------+
| dob | n_days |
+--------------------+-------------+
|1980-08-09 13:34:10 | 29442.5654 |
|2005-12-15 23:10:00 | 38701.6528 |
|2020-02-26 15:56:00 | 43887.6639 |
+--------------------+-------------+
Query:
SELECT DATE_PART('day', dob -'1899-12-30 00:00:00'::TIMESTAMPTZ) AS n_days
FROM my_date;
Returns only whole day count:
n_days
---------
29441
38701
43887

Consider:
extract(epoch from dob - '1899-12-30 00:00:00'::timestamptz) / 60 / 60 / 24
Rationale:
the timestamp substraction gives you an interval that represents the difference between the timestamps
extract(epoch from ...) turns this to a number of seconds
all that is left to do is divide by the number of seconds that there is in a day
Demo on DB Fiddle:
with t as (
select '1980-08-09 13:34:10'::timestamptz dob
union all select '2005-12-15 23:10:00'::timestamptz
union all select '2020-02-26 15:56:00'::timestamptz
)
select
dob,
extract(epoch from dob - '1899-12-30 00:00:00'::timestamptz) / 60 / 60 / 24 as n_days
from t
dob | n_days
:--------------------- | :-----------------
1980-08-09 13:34:10+01 | 29442.52372685185
2005-12-15 23:10:00+00 | 38701.965277777774
2020-02-26 15:56:00+00 | 43887.66388888889

Related

Group by data by 5 mintues sample

I am having a table like this and I am storing epoch time as one column. I want to use epoch time column to group by data by 5 min
-------------------
Name | epoch_time |
A | 1585977780 |
B | 1585977780 |
C | 1585978080 |
-------------------
You have Unix time, so you can use arithmetic:
select floor(epoch_time / (60 * 5)) * 60 * 5 as minutes_5, count(*)
from t
group by floor(epoch_time / (60 * 5)) * 60 * 5

How can I aggregate values based on an arbitrary monthly cycle date range in SQL?

Given a table as such:
# SELECT * FROM payments ORDER BY payment_date DESC;
id | payment_type_id | payment_date | amount
----+-----------------+--------------+---------
4 | 1 | 2019-11-18 | 300.00
3 | 1 | 2019-11-17 | 1000.00
2 | 1 | 2019-11-16 | 250.00
1 | 1 | 2019-11-15 | 300.00
14 | 1 | 2019-10-18 | 130.00
13 | 1 | 2019-10-18 | 100.00
15 | 1 | 2019-09-18 | 1300.00
16 | 1 | 2019-09-17 | 1300.00
17 | 1 | 2019-09-01 | 400.00
18 | 1 | 2019-08-25 | 400.00
(10 rows)
How can I SUM the amount column based on an arbitrary date range, not simply a date truncation?
Taking the example of a date range beginning on the 15th of a month, and ending on the 14th of the following month, the output I would expect to see is:
payment_type_id | payment_date | amount
-----------------+--------------+---------
1 | 2019-11-15 | 1850.00
1 | 2019-10-15 | 230.00
1 | 2019-09-15 | 2600.00
1 | 2019-08-15 | 800.00
Can this be done in SQL, or is this something that's better handled in code? I would traditionally do this in code, but looking to extend my knowledge of SQL (which at this stage, isnt much!)
Click demo:db<>fiddle
You can use a combination of the CASE clause and the date_trunc() function:
SELECT
payment_type_id,
CASE
WHEN date_part('day', payment_date) < 15 THEN
date_trunc('month', payment_date) + interval '-1month 14 days'
ELSE date_trunc('month', payment_date) + interval '14 days'
END AS payment_date,
SUM(amount) AS amount
FROM
payments
GROUP BY 1,2
date_part('day', ...) gives out the current day of month
The CASE clause is for dividing the dates before the 15th of month and after.
The date_trunc('month', ...) converts all dates in a month to the first of this month
So, if date is before the 15th of the current month, it should be grouped to the 15th of the previous month (this is what +interval '-1month 14 days' calculates: +14, because the date_trunc() truncates to the 1st of month: 1 + 14 = 15). Otherwise it is group to the 15th of the current month.
After calculating these payment_days, you can use them for simple grouping.
I would simply subtract 14 days, truncate the month, and add 14 days back:
select payment_type_id,
date_trunc('month', payment_date - interval '14 day') + interval '14 day' as month_15,
sum(amount)
from payments
group by payment_type_id, month_15
order by payment_type_id, month_15;
No conditional logic is actually needed for this.
Here is a db<>fiddle.
You can use the generate_series() function and make a inner join comparing month and year, like this:
SELECT specific_date_on_month, SUM(amount)
FROM (SELECT generate_series('2015-01-15'::date, '2015-12-15'::date, '1 month'::interval) AS specific_date_on_month)
INNER JOIN payments
ON (TO_CHAR(payment_date, 'yyyymm')=TO_CHAR(specific_date_on_month, 'yyyymm'))
GROUP BY specific_date_on_month;
The generate_series(<begin>, <end>, <interval>) function generate a serie based on begin and end with an specific interval.

How to convert a number (YYMMDDhhmmss) into timestamp in Oracle's SQL?

I am trying to calculate a difference between systimestamp and a number in Oracle's SQL.
The number format is YYMMDDhhmmss (For example 190903210000). This number is held in a table and is based on 24 hour timezone.
I am trying to calculate a difference that number and system timestamp in seconds.
I have to use a select sentence as below:
SELECT X FROM table
Then I need to calculate a difference (SYSTEMTIMESTAMP - X)
My system timestamp is formatted like 03-SEP-19 06.21.49.817757 PM +00:00
Could you please advise me on the right approach?
To convert a NUMBER to a TIMESTAMP you can use an expression like TO_TIMESTAMP(TO_CHAR(...)). To compute the number of seconds between two timestamps, one solution is to cast both to dates, and substract them : you will get a (decimal) result in days, which can be then converted to seconds.
Consider:
(
CAST(systimestamp AS DATE)
- CAST(TO_TIMESTAMP(TO_CHAR(190903210000), 'YYMMDDhh24miss') AS DATE)
) * 60 * 60 * 24
However, since your numeric representation of the timestamp does not contain fractional seconds (nor timezone), it would probably be simpler to convert directly to a DATE, which would remove the need to CAST it afterwards, hence:
(
CAST(systimestamp AS DATE)
- TO_DATE(TO_CHAR(190903210000), 'YYMMDDhh24miss')
) * 60 * 60 * 24
Demo on DB Fiddle:
SELECT
systimestamp,
190903210000 num,
TO_TIMESTAMP(TO_CHAR(190903210000), 'YYMMDDhh24miss') num_as_timestamp,
(
CAST(systimestamp AS DATE) -
CAST(TO_TIMESTAMP(TO_CHAR(190903210000), 'YYMMDDhh24miss') AS DATE)
) * 60 * 60 * 24 diff
FROM DUAL;
SYSTIMESTAMP | NUM | NUM_AS_TIMESTAMP | DIFF
:---------------------------------- | -----------: | :------------------------------ | ---:
03-SEP-19 09.13.39.989343 PM +01:00 | 190903210000 | 03-SEP-19 09.00.00.000000000 PM | 819
SELECT
systimestamp,
190903210000 num,
TO_DATE(TO_CHAR(190903210000), 'YYMMDDhh24miss') num_as_date,
(
CAST(systimestamp AS DATE)
- TO_DATE(TO_CHAR(190903210000), 'YYMMDDhh24miss')
) * 60 * 60 * 24 diff
FROM DUAL;
SYSTIMESTAMP | NUM | NUM_AS_DATE | DIFF
:---------------------------------- | -----------: | :----------------- | ----------------------------------------:
03-SEP-19 09.20.44.524445 PM +01:00 | 190903210000 | 03-SEP-19 21:00:00 | 1243.999999999999999999999999999999999996
Try this:
select 190903210000 n,
to_timestamp(to_char(190903210000), 'YYMMDDHH24MISS') ts
from dual;
In SQLFiddle and the Oracle documentation.

SQLite: Sum of differences between two dates group by every date

I have a SQLite database with start and stop datetimes
With the following SQL query I get the difference hours between start and stop:
SELECT starttime, stoptime, cast((strftime('%s',stoptime)-strftime('%s',starttime)) AS real)/60/60 AS diffHours FROM tracktime;
I need a SQL query, which delivers the sum of multiple timestamps, grouped by every day (also whole dates between timestamps).
The result should be something like this:
2018-08-01: 12 hours
2018-08-02: 24 hours
2018-08-03: 12 hours
2018-08-04: 0 hours
2018-08-05: 1 hours
2018-08-06: 14 hours
2018-08-07: 8 hours
You can try this, use CTE RECURSIVE make a calendar table for every date start time and end time, and do some calculation.
Schema (SQLite v3.18)
CREATE TABLE tracktime(
id int,
starttime timestamp,
stoptime timestamp
);
insert into tracktime values
(11,'2018-08-01 12:00:00','2018-08-03 12:00:00');
insert into tracktime values
(12,'2018-09-05 18:00:00','2018-09-05 19:00:00');
Query #1
WITH RECURSIVE cte AS (
select id,starttime,date(starttime,'+1 day') totime,stoptime
from tracktime
UNION ALL
SELECT id,
date(starttime,'+1 day'),
date(totime,'+1 day'),
stoptime
FROM cte
WHERE date(starttime,'+1 day') < stoptime
)
SELECT strftime('%Y-%m-%d', starttime),(strftime('%s',CASE
WHEN totime > stoptime THEN stoptime
ELSE totime
END) -strftime('%s',starttime))/3600 diffHour
FROM cte;
| strftime('%Y-%m-%d', starttime) | diffHour |
| ------------------------------- | -------- |
| 2018-08-01 | 12 |
| 2018-09-05 | 1 |
| 2018-08-02 | 24 |
| 2018-08-03 | 12 |
View on DB Fiddle

PostgreSQL query group by two "parameters"

I've been trying to figure out the following PostgreSQL query with no success for two days now.
Let's say I have the following table:
| date | value |
-------------------------
| 2018-05-11 | 0.20 |
| 2018-05-11 | -0.12 |
| 2018-05-11 | 0.15 |
| 2018-05-10 | -1.20 |
| 2018-05-10 | -0.70 |
| 2018-05-10 | -0.16 |
| 2018-05-10 | 0.07 |
And I need to find out the query to count positive and negative values per day:
| date | positives | negatives |
------------------------------------------
| 2018-05-11 | 2 | 1 |
| 2018-05-10 | 1 | 3 |
I've been able to figure out the query to extract only positives or negatives, but not both at the same time:
SELECT to_char(table.date, 'DD/MM') AS date
COUNT(*) AS negative
FROM table
WHERE table.date >= DATE(NOW() - '20 days' :: INTERVAL) AND
value < '0'
GROUP BY to_char(date, 'DD/MM'), table.date
ORDER BY table.date DESC;
Can please someone assist? This is driving me mad. Thank you.
Use a FILTER clause with the aggregate function.
SELECT to_char(table.date, 'DD/MM') AS date,
COUNT(*) FILTER (WHERE value < 0) AS negative,
COUNT(*) FILTER (WHERE value > 0) AS positive
FROM table
WHERE table.date >= DATE(NOW() - '20 days'::INTERVAL)
GROUP BY 1
ORDER BY DATE(table.date) DESC
I would simply do:
select date_trunc('day', t.date) as dte,
sum( (value < 0)::int ) as negatives,
sum( (value > 0)::int ) as positives
from t
where t.date >= current_date - interval '20 days'
group by date_trunc('day', t.date),
order by dte desc;
Notes:
I prefer using date_trunc() to casting to a string for removing the time component.
You don't need to use now() and convert to a date. You can just use current_date.
Converting a string to an interval seems awkward, when you can specify an interval using the interval keyword.