SQL difference between two days in hours per day - sql

I have input: '2017-02-02 11:00:00' and '2017-02-13 15:00:00'
I want to know the difference in hours between these days, but there is a twist: I need to know the hours for each day.
the result would look something like
'2017-02-02', '13'
'2017-02-03', '24'
...
'2017-02-13', '15'
If it was only hours then I would use DATEDIFF, but assuming the twist I have no idea how to start. I would appreciate any idea.
ps: there can be different inputs, for example '2017-02-02 11:00:00' and '2017-02-02 15:00:00'

You can use generate_series to generate all the datetimes with an interval of 1 hour between the start and end dates. Then you can group by the date part to get the number of hours worked each day.
select dt_time::date,count(*) as hours_by_day
from (select generate_series('2017-02-02 11:00:00','2017-02-13 15:00:00',interval '1 hour') as dt_time
) x
group by dt_time::date
order by 1

One way uses generate_series() and some arithmetic:
with dates as (
select gs.dte, $date1 as dte1, $date2 as dte2
from generate_series(date_trunc('day', $date1), date_trunc('day', $date2), interval '1 day') gs(dte)
)
select gs.dte,
(case when dte1 > gs.dte and dte2 < gs.dte then 24
when date_trunc('day', dte2) = gs.dte then 24 - extract(hour from dte1)
else extract(hour from dte2)
end) as hours
from dates;

Related

Take last hour and group it by 1 minute

I was wondering if you can help me write a query that should just SELECT count(*) but only include data from last hour and group it by minute.
So I have a table that has a createdts so I have the date there. I just want to see how many entries I have in the last hour, but group COUNT(*) per minute.
SELECT COUNT(*) FROM mytable
WHERE createdts >= now()::date - interval '1 hour'
GROUP BY 'every minute'
DATE_TRUNC() does this:
SELECT DATE_TRUNC('minute', createdts), COUNT(*)
FROM mytable
WHERE createdts >= now()::date - interval '1 hour'
GROUP BY DATE_TRUNC('minute', createdts)
ORDER BY DATE_TRUNC('minute', createdts);

Select Data From Multiple Days Between Certain Times (Spanning 2 days)

I need to know how many entries appear in my DB for the past 7 days with a timestamp between 23:00 & 01:00...
The Issue I have is the timestamp goes across 2 days and unsure if this is even possible in the one query.
So far I have come up with the below:
select trunc(timestamp) as DTE, extract(hour from timestamp) as HR, count(COLUMN) as Total
from TABLE
where trunc(timestamp) >= '12-NOV-19' and
extract(hour from timestamp) in ('23','00','01')
group by trunc(timestamp), extract(hour from timestamp)
order by 1,2 desc;
The result I am hoping for is something like this:
DTE | Total
20-NOV-19 5
19-NOV-19 4
18-NOV-19 4
17-NOV-19 6
Many thanks
Filter on the day first comparing it to TRUNC( SYSDATE ) - INTERVAL '7' DAY and then consider the hours by comparing the timestamp to itself truncated back to midnight with an offset of a number of hours.
select trunc(timestamp) as DTE,
extract(hour from timestamp) as HR,
count(COLUMN) as Total
from TABLE
WHERE timestamp >= TRUNC( SYSDATE ) - INTERVAL '7' DAY
AND ( timestamp <= TRUNC( timestamp ) + INTERVAL '01:00' HOUR TO MINUTE
OR timestamp >= TRUNC( timestamp ) + INTERVAL '23:00' HOUR TO MINUTE
)
group by trunc(timestamp), extract(hour from timestamp)
order by DTE, HR desc;
Subtract or add an hour to derive the date. I'm not sure what date you want to assign to each period, but the idea is:
select trunc(timestamp - interval '1' hour) as DTE,
count(*) as Total
from t
where trunc(timestamp - interval '1' hour) >= DATE '2019-11-12' and
extract(hour from timestamp) in (23, 0)
group by trunc(timestamp - interval '1' hour)
order by 1 desc;
Note: If you want times between 11:00 p.m. and 1:00 a.m., then you want the hour to be 23 or 0.

Converting Recursive Query from Week over Week to Month over Month

I have a recursive query that provides the number of orders placed week over week (week_no, week_start, and week_end). I'd like to create a similar breakdown for a month over month analysis.
WITH recursive weeks (week_start, week_end, time_end, weekno) AS (
VALUES ('2015-12-27'::date, '2016-01-02'::date, '2016-04-02'::date, 1)
UNION ALL
SELECT (week_end + interval '1 day')::date,
(CASE
WHEN (week_end + interval '7 days')::date > time_end THEN time_end
ELSE (week_end + interval '7 days')::date
END)::date,
time_end,
weekno+1
FROM weeks
WHERE time_end > week_end)
Any help would be greatly appreciated.
Why would you use a recursive query for this? Use generate_series():
select g.week_start, g.week_start + interval '6 day' as week_end,
row_number() over (order by g.week_start) as weeknum
from generate_series('2015-12-27'::timestamp,
'2016-01-02'::timestamp,
interval '1 week'
) g(week_start);
The equivalent for months would be like:
select g.month_start, g.month_start + interval '1 month' - interval '1 day' as month_end,
row_number() over (order by g.month_start) as monthnum
from generate_series('2015-12-01'::timestamp,
'2016-01-01'::timestamp,
interval '1 month'
) g(month_start);

Sqlite Timestamp various compare

How is it possible to compare a TIMESTAMP column by date or time?
I try to retrieve records that difference of TIMESTAMP column and NOW are a week or 2 hours or less 30 minutes.
SELECT FROM Tcase WHERE (date_time-datetime('now'))<7day
SELECT FROM Tcase WHERE (date_time-datetime('now'))<1hour
Example
SELECT * FROM Tcase
WHERE datetime(date_time,'-7 day', '+5 hour', '+10 minute') >= date('now')
Edit
SELECT * FROM Tcase
date('now') > datetime(date_time,'+7 day')
OR date('now') < datetime(date_time,'-7 day')
See SQLite date functions
SQLFiddle example

Generating a series from a predefined date (PG)

I'm trying to generate a series of monthly dates from a starting date, which happens to be the date of the oldest user in my users table.
Whilst I can select some dates quite easily;
SELECT generate_series(
now(),
now() + '5 months'::interval,
'1 month'::interval);
and can select the date I need to start at:
SELECT to_date( to_char(CAST(min(created_at) AS DATE),'yyyy-MM') || '-01','yyyy-mm-dd') from users
How can I combine the two so that I'm selecting every month up until now?
Turns out, it can be even simpler. :)
SELECT generate_series(
date_trunc('year', min(created_at))
, now()
, interval '1 month') AS month;
FROM users;
More about date_trunc in the manual.
Or, if you actually want the data type date instead of timestamp with time zone:
SELECT generate_series(
date_trunc('year', min(created_at))
, now()
, interval '1 month')::date AS month;
FROM users;
Turns out it's pretty simple:
SELECT generate_series(
(SELECT to_date( to_char(CAST(min(created_at) AS DATE),'yyyy-MM') || '-01','yyyy-mm-dd') from users),
now(),
'1 month'::interval) as month;