SQLite - GROUP BY rows, only when certain columns match another - sql

I am building a schedule app, which uses a predefined API from my school. Each lesson has it's times declared for each hour in a single row. A short example:
Table lessons:
Subject Id Start End
English 111 09:30 10:30
English 111 10:30 11:30
Dutch 120 12:30 13:30
Java 109 14:30 15:30
English 111 15:30 16:30
To retrieve the lessons, and to show them inside my app, I use the following query:
SELECT MIN(start), MAX(end) FROM lessons ORDER BY Start GROUP BY Id
It works pretty well, however when a student has the same two lessons with the same Id on a day, in this case English, the SQL query will show it as:
English 09:30 - 16:30
In this case we have a problem, since English doesn't take 6 hours, and I want to show it as:
English 09:30 - 11:30
English 15:30 - 16:30
So my question is:
What query should I use to only GROUP BY lessons when the start or end values equals another row with the same id, to avoid wrong times?, I could do this programmaticly but I really prefer to do this using SQL.
EDIT:
I am grouping by, because I don't want to show each lesson as a seperate row, because my school defines every lessonhour as a single row.

This will do the job
with cte as
(
select subject,id,start,end
from mytable t
where not exists
(
select null
from mytable t2
where t2.id = t.id
and t2.end = t.start
)
union all
select t.subject,t.id,cte.start,t.end
from cte
join mytable t
on t.id = cte.id
and t.start = cte.end
)
select subject,id,start,max(end) as end
from cte
group by subject,id,start
+---------+-----+-------+-------+
| subject | id | start | end |
+---------+-----+-------+-------+
| Dutch | 120 | 12:30 | 13:30 |
+---------+-----+-------+-------+
| English | 111 | 09:30 | 11:30 |
+---------+-----+-------+-------+
| English | 111 | 15:30 | 16:30 |
+---------+-----+-------+-------+
| Java | 109 | 14:30 | 15:30 |
+---------+-----+-------+-------+

Related

How to list uniques entries in a column and in the corresponding times of their repition in next column?

I have a hospital database which looks something like this:
id | patient_name | admitDate | DischargeDate
1 | john | 3/01/2011 08:50 | 5/01/2011 12:50
2 | lisa | 3/01/2011 09:50 | 4/01/2011 13:50
3 | ron | 5/01/2012 10:40 | 10/01/2012 03:50
4 | howard | 6/02/2013 08:05 | 10/02/2013 08:50
5 | john | 6/02/2013 12:04 | 7/02/2013 01:50
The admitDate is same for many entries (time may be different). I want to find out how many patients were admitted on any particular day so if I do this:
select distinct left(admitDate,10),
(select count(distinct left(admitDate,10) ) from hospital)
from hospital
I get output as all distinct admit dates in 1st column and same value 5 in all rows of second column. How do I make it so that only corresponding repetition count is found in 2nd column and not the count of entire admitDate set.
datatype of admitdate is varchar(50)
I am using left function because I only have to find out uniqueness in dates not in time.
Expected result:
admitDate | Count
3/01/2011 | 2
5/01/2012 | 1
6/02/2013 | 2
Current result:
admitDate | Count
3/01/2011 | 5
5/01/2012 | 5
6/02/2013 | 5
If your admitDate Column has Time too, you need use Convert() function to eliminate the time to group by your data per each day:
Select CONVERT(date, admitDate), count(*)
from hospital
group by CONVERT(date, admitDate);
If you use Varchar instead of Date data type for your admitDate column you can try this:
SELECT LEFT(admitDate, charindex(' ', admitDate) - 1) as ADMITDATE , count(*) as COUNTER
from hospital
group by LEFT(admitDate, charindex(' ', admitDate) - 1) ;
or:
SELECT convert(date, (convert(datetime2, admitDate,103)) ), count(*)
from hospital
group by convert(date, (convert(datetime2, admitDate,103)) )

SQL Find difference between rows with unique IDs

I have a table with two rows, one has a start and one an end time with the same id.
For example
ID | Time
12345 | 12-12-18 12:00
12345 | 12-12-18 12:12
54321 | 12-12-18 11:30
54321 | 12-12-18 11:35
How would i go around getting the output
ID | Time
12345 | 12
54321 | 5
Guessing Lag or Over?
You seem to want an aggregation:
select id,
datediff(minute, min(time), max(time)) as diff_minutes
from t
group by id;
You can also selfjoin as Ezlo also wrote:
select a.ID, datediff(minute,MIN(a.time1),max(x.time1) ) as maxtime
from #test a inner join #test x on A.id = x.id group by a.id
But seems like Gordons is much simpler :)

Set end time of first row as start time of next row

I have table with data like:
Id | Start | End | Used
----------------------------------------
1 | 27-04-17 2:00 |27-04-17 0:00 | 1:30
---------------------------------------
2 | 27-04-17 2:00 |27-04-17 0:00 | 23:00
---------------------------------------
3 | 27-04-17 2:00 |27-04-17 0:00 | 1:00
---------------------------------------
4 | 28-04-17 2:00 |28-04-17 0:00 | 0:30
---------------------------------------
5 | 30-04-17 2:00 |30-04-17 0:00 | 3:30
---------------------------------------
I want to set it like
Id | Start | End | Used
----------------------------------------
1 | 27-04-17 2:00 |27-04-17 3:30 | 1:30
---------------------------------------
2 | 27-04-17 3:30 |28-04-17 2:30 | 23:00
---------------------------------------
3 | 28-04-17 2:30 |28-04-17 3:30 | 1:00
---------------------------------------
4 | 28-04-17 3:30 |28-04-17 4:00 | 0:30
---------------------------------------
5 | 30-04-17 2:00 |30-04-17 4:30 | 2:30
---------------------------------------
I want to set End time of previous id as Start time of next id, where Start time of first id is set by user.End time is just sum of Start time and Used Time I am using vb.net data table and SQL server for database. To set value in first row I am using SQL function DATEADD(), through which two hours are added and same function for adding time in end date.
I want to copy my End date of previous row in next row, so that whole calculation works properly.Can I do it through SQL Only? or I will need a function to do it in Vb.net Data table from where it will be used for reports. Note: Id sequence can be changed Thanks for help.
Below recursive CTE logic will give you the desired output.
create table #tmp
(
ID int,
starttime datetime2,
endtime datetime2,
used varchar(5)
)
insert into #tmp values
(1,'17-Apr-2017 2:00','17-Apr-2017','1:30'),
(2,'17-Apr-2017 2:00','17-Apr-2017','2:00'),
(3,'17-Apr-2017 2:00','17-Apr-2017','1:00'),
(4,'17-Apr-2017 2:00','17-Apr-2017','0:30'),
(5,'28-Apr-2017 2:00','28-Apr-2017','3:30')
;with CTE as (
select ID,starttime,endtime,used,levels FROM
(select row_number() over (partition by cast(starttime as date) order by starttime) RID,ID,starttime,dateadd(hour,cast(substring(used,1,charindex(':',used)-1) as int),dateadd(mi,cast(right(used,2) as int),starttime))
endtime,used,0 levels
from #tmp ) T
where RID=1
union all
select T.ID,C.endtime,dateadd(hour,cast(substring(T.used,1,charindex(':',T.used)-1) as int),dateadd(mi,cast(right(T.used,2) as int),C.endtime))
endtime,T.used,C.levels+1
from CTE C inner join #tmp T on T.ID=C.ID+1
where datediff(d,C.starttime,T.starttime)=0
)
select ID,StartTime,EndTime,Used from CTE order by ID
drop table #tmp

T-SQL Query for counting ongoing events per given intervals

We have events with a date range:
Event | Begin | End
------|-------|------
a | 11:30 | 12:15
b | 10:30 | 13:15
c | 11:30 | 13:30
Visualized as a timetable:
a) |---|
b) |---------------|
c) |-----------|
|-----|-----|-----|-----|
10:00 11:00 12:00 13:00 14:00
We want an efficient query for counting ongoing events on given timestamps. In this example we want them per hour. Like this:
Time | OnGoing
-----------------|--------
2014-02-06 10:00 | 0
2014-02-06 11:00 | 1
2014-02-06 12:00 | 3
2014-02-06 13:00 | 2
2014-02-06 14:00 | 0
You can create a driver table with all hours, then join to that:
;WITH cal AS (SELECT CAST('10:00:00' AS TIME) dt
UNION ALL
SELECT DATEADD(hour,1,dt)
FROM cal
WHERE dt < '14:00:00')
SELECT dt, COUNT(DISTINCT Event) OnGoing
FROM cal a
LEFT JOIN Table1 b
ON a.dt BETWEEN b.[Begin] AND b.[End]
GROUP BY dt
Demo: SQL Fiddle
Adjust the range in the cal cte to fit your preferences. I notice your sample output shows a datetime, so you could cast begin and end as TIME in your join and add a DATE portion to your select and group by, or you could alter the cte to be full datetime. If spanning more than 100 units in your cte, you'll need to add OPTION (MAXRECURSION 0) to the very end of your query.

DENSE RANK in both ways

Thanks to answers to my previous question, I could establish this kind of query
http://sqlfiddle.com/#!4/3e6f9/7/0
The result table shows work time right before each pause data.
From this, I would like to add another column which shows "work time right after each pause data", resulting as below.
STAFF | MAX(...) | START_TIME | END_TIME | MIN(...)
------+----------+------------+----------+---------
GC01 | 12:00 | 12:03 | 12:07 | 12:10
GC01 | 12:20 | 12:25 | 12:35 | 12:40
GC02 | 12:33 | 12:35 | 12:45 | (null)
I think this is what you're looking for. You need to rejoin to the work table:
select p.staff,
max(w.work_time) keep (dense_rank first order by w.work_time desc),
p.start_time, p.end_time,
min(w2.work_time) keep (dense_rank first order by w2.work_time)
from pause p
join work w
on p.staff = w.staff
and p.start_time >= w.work_time
left join work w2
on p.staff = w2.staff
and p.end_time <= w2.work_time
group by p.staff, p.start_time, p.end_time
Updated SQL Fiddle