MySQL Split date range into separate records - mysql

I need to split one date record into a several date records (dynamically, depending on a date range that overlaps) like this in MySQL:
input data:
level |start_time |end_time
1 2018-12-24 09:00:00 2018-12-25 09:00:00
Output result should look like:
level |start_time |end_time
1 2018-12-24 09:00:00 2018-12-25 00:00:00
1 2018-12-25 00:00:00 2018-12-25 09:00:00
Any help is much appreciated.

Try to initiate your sql code like this. It's a long shot but I hope it get you started somewhere.
select tx.col1
FROM
( (
SELECT start_time as col1 from t1
)
UNION
(
SELECT end_time as col1 from t1
)
) as tx

Related

Group by Day of Month

Say I have a table logins with just the bigint id ,a date_login of type datetime, and fk_user of type bigint. I need to select the first and last logins for each day of the month in the specified year.
I was guessing something like:
select *theDayOfTheMonth*,min(date_login), max(date_login)
from logins
where year(date_login) = *theYearInput* and
and fk_user = *theKeySpecified* and
month(date_login) = *theMonthInput*
group by *theDayOfTheMonth*
but I don't know how to group by that day of month. How can I do this?
You were close. It will look something like this:
SELECT DATE(date_login) as dayOfMonth, min(date_login), max(date_login)
FROM logins
WHERE year(date_login) = #theYearInput
and fk_user = #theKeySpecified
and month(date_login) = #theMonthInput
GROUP BY DATE(date_login)
Alternatively you might use the Day() function instead, which should still work because the query is limited to a single specific month via conditions in the WHERE clause:
SELECT DAY(date_login) as dayOfMonth, min(date_login), max(date_login)
FROM logins
WHERE year(date_login) = #theYearInput
and fk_user = #theKeySpecified
and month(date_login) = #theMonthInput
GROUP BY DAY(date_login)
Functionally, given these conditions the only difference between them is the format of the first column in the output. However, if you later need to expand across larger date ranges the first version will handle that better.
The function that you are looking for is DayOfMonth()
create table logins(
fk_user bigint,
date_login datetime);
insert into logins values
(1,'2022-01-01 09:00:00'),(1,'2022-01-01 18:00:00'),(1,'2022-01-02 08:00:00'),(1,'2022-01-02 16:00:00')
select
DayOfMonth(date_login) day,
min(date_login) first_login,
max(date_login) last_login
from logins
where year(date_login) = 2022
and fk_user = 1 and
month(date_login) = 1
group by DayOfMonth(date_login);
day | first_login | last_login
--: | :------------------ | :------------------
1 | 2022-01-01 09:00:00 | 2022-01-01 18:00:00
2 | 2022-01-02 08:00:00 | 2022-01-02 16:00:00
db<>fiddle here

SQL Count events with duration per hour

I have data of an event with duration (say, eating a meal at a restaurant) and I want to know for any given hour how many events were taking place. The data looks like this:
Event | Start Time | End Time
-----------------------------------------
1 | 12:03 | 14:20
2 | 12:30 | 12:50
3 | 13:05 | 14:45
4 | 14:01 | 14:49
I also have "Duration" available as an alternative to "End Time". The result I'm looking for would be like this:
Hour | Count
-----------------------
12 | 2
13 | 2
14 | 3
During hour 12, there were two events happening (1 & 2), hour 13 also had two events (1 & 3) and hour 14 had three events (1, 3, & 4).
I can do this programmatically with a loop. I can count when the events start (or end) in SQL. But I'd really like to bridge the gap and do this in SQL, but I can't think of a way.
One possible solution (works with MySQL v5.6+ and SQLite3):
create table hours(Hour int);
insert into hours values
(0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),
(13),(14),(15),(16),(17),(18),(19),(20),(21),(22),(23);
create table log(Event int,StartTime varchar(5),EndTime varchar(5));
insert into log values
(1,'12:03','14:20'),
(2,'12:30','12:50'),
(3,'13:05','14:45'),
(4,'14:01','14:49');
-- ------------------------------------------------------------------------------
select Hour,count(Event) Count
from log join hours
on Hour between substr(StartTime,1,2) and substr(EndTime,1,2)
group by Hour;
If you are running MySQL 8.0, you could use UNION ALL, window functions and aggregation, like so:
select hr, sum(sum(cnt)) over(order by hr) cnt
from (
select hour(start_time) hr, 1 cnt from mytable
union all select hour(end_time) + 1, -1 from mytable
) t
group by hr
Demo on DB Fiddle:
hr | cnt
-: | --:
12 | 2
13 | 2
14 | 3
15 | 0
If you do not have MySql 8, then create a table hour:
CREATE TABLE hour (
hr INT PRIMARY KEY
);
INSERT INTO hour(hr) VALUES
(0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),
(12),(13),(14),(15),(16),(17),(18),(19),(20),(21),(22),(23);
And then:
select h.hr, count(*) as cnt from hour h
join mytable m on h.hr between hour(m.Start_Time) and hour(m.End_Time)
group by hr
order by hr
;
See Db-Fiddle

group by year on multiple date columns mysql

I have table as following:
hours | ... | task_assigned | task_deadline | task_completion
----------------------------------------------------------------
123 | ... | 2019-08-01 | - | -
234 | ... | - | 2018-08-01 | 2019-08-01
145 | ... | 2017-08-01 | 2017-08-01 | 2018-01-01
I want to calculate total hours for each year, i.e. grouping by year.
Currently I'm only taking into account task_completion field.
If there's no value in task_completion field, the record is not included in SUM calculation.
To elaborate further, say for year 2019, row 1 and 1 both should be considered. Hence the total hours should be 123 + 234 = 357.
And for year 2018, row 2 and 3.
Similarly, for year 2017, row 3.
SELECT YEAR(task_completion) as year, ROUND(SUM(total_hours), 2) as hours
FROM task
GROUP BY year
HAVING year BETWEEN '$year_from' AND '$year_to'
The resultset:
year | hours
--------------------
2017 | <somevalue>
2018 | <somevalue>
2019 | <somevalue>
How can I include other two date fields too?
You want to consider each row once for each of its years. Use UNION to get these years:
select year, round(sum(total_hours), 2) as hours
from
(
select year(task_assigned) as year, total_hours from task
union
select year(task_deadline) as year, total_hours from task
union
select year(task_completion) as year, total_hours from task
) years_and_hours
group by year
having year between $year_from and $year_to
order by year;
If you want to consider a row with one year twice or thrice also as often in the sum, then change UNION to UNION ALL.
Basically, you want to unpivot the data. I will assume that the - represents a NULL value and your dates are real dates.
select year(dte) as year, sum(total_hours) as hours
from ((select task_assigned as dte, total_hours
from task
) union all
(select task_deadline, total_hours
from task
) union all
(select task_completion, total_hours
from task
)
) d
where dte is not null
group by year(dte)
order by year(dte);
Based on your sample data, the round() is not necessary so I removed it.
If you want to filter for particular years, the filtering should be in a where clause -- so it filters the data before aggregation.
Change the where to:
where year(dte) >= ? and year(dte) <= ?
or:
where dte >= ? and dte <= ?
to pass in the dates.
The ? are for parameter placeholders. Learn how to use parameters rather than munging query strings.
This answer is no langer valid with the updated request.
If I understand correctly, you want to use task_assigned if the task_completion is still null. Use COALEASCE for this.
SELECT
YEAR(COALESCE(task_completion, task_assigned)) as year,
ROUND(SUM(total_hours), 2) as hours
FROM task
GROUP BY year
HAVING year BETWEEN $year_from AND $year_to
ORDER BY year;
(I don't think you actually want to use task_deadline, too, for how could a task get completed before getting assigned first? If such can occur, then include it in the COALESCE expression. Probably: COALESCE(task_completion, task_assigned, task_deadline)` then.)

SQL : Select record by passing a date within the range

I have table Structure like this
id from to
1 2014-02-01 00:00:00 2014-02-28 00:00:00
2 2014-01-01 00:00:00 2014-01-30 00:00:00
3 2014-03-01 00:00:00 2014-03-30 00:00:00
and There is no date range overlapping in this
I'm trying to select record by passing a date which lie within the range
eg. passing date '2014-01-16' should return the record with id=2 as it falls within the range of the dates in this row, please guide me to solve this.
Thanks in advance
SELECT * FROM mytable
WHERE '2014-01-16' BETWEEN `from` AND `to`
SQLFiddle
SELECT id FROM table WHERE from <= '2014-01-16' AND to >= '2014-01-16'

MySQL get set of data with distinct values

I have a simple problem; one which I thought would have a simple solution. Imagine a Table like this:
Name Timestamp Data
Bob 2011-01-01 01:00:00 Hi
Alice 2011-02-02 02:00:00 Hello
Alice 2011-03-03 03:00:00 Hello
Bob 2011-04-04 04:00:00 Bye
Charlie 2011-05-05 05:00:00 Cheese
Charlie 2011-06-06 06:00:00 Toast
All I want is to be able to run a query that shows the most recent entry for each Name. So with the above table, I would like an output like this:
Name Timestamp Data
Bob 2011-04-04 04:00:00 Bye
Alice 2011-03-03 03:00:00 Hello
Charlie 2011-06-06 06:00:00 Toast
Ordered by Data. I can't figure out how to do this. I though I could just do:
SELECT DISTINCT(Name), timestamp, Data FROM Table ORDER BY Data
But this doesn't work. Any help would be most appreciated,
Cheers.
Use this query to get last unique values,
SELECT * FROM (select * from `table` order by date DESC) t group by name
SELECT DISTINCT Name, Timestamp, Data
FROM Table
WHERE Timestamp =
(
SELECT MAX(Timestamp)
FROM Table AS tempTable
WHERE Table.Name = tempTable.Name
)
select t1.* from table as t1
inner join (
select name,max(`timestamp`) as `timestamp`
from table
group by name) as t2
on t1.name = t2.name and t1.`timestamp` = t2.`timestamp`