This query returns me the list of room #077 that is occupied on specific day; how do I reverse this query and show only times that are NOT in the database between 07:00:00 and 22:00:00? (30 minutes intervals) and each class should take only 1 hour and 30 minutes
select
*
from
(select
rooms.id, rooms.number, rooms.building, rooms.capacity
from
rooms) R1,
(select
exam_schedules.room_id,
exam_schedules.day,
exam_schedules.start_time,
exam_schedules.end_time
from
exam_schedules) R2
where
R2.room_id = R1.id and R2.day = 'tuesday' and R1.number = '077'
This is the result:
ID number Bulding Capacity room_id day start_time end_time
1 077 ACT 12 1 tuesday 10:30:00 12:00:00
But I need the result that is shown below (which is pretty much shows the AVAILABLE times that are NOT occupied by exams AND could no be occupied by other exam since it might cause scheduling issues: for example if exam is already scheduled at 10.30, only 9.00 (not 9.30) should be shown since if 9.30 is shown - it will cause the conflict: 9.30+1.30 = 11.00 - but I already have the 10.30-12.00 scheduled for this room)
1 077 ACT 12 1 tuesday 07:00:00
1 077 ACT 12 1 tuesday 07:30:00
1 077 ACT 12 1 tuesday 08:00:00
1 077 ACT 12 1 tuesday 08:30:00
1 077 ACT 12 1 tuesday 09:00:00
//note that time frame from 9.30-10.30 is not available since there is a class at 10.30 scheduled already
//note that time period 10.30-<12.00 not shown since class is already scheduled for this timeframe 10.30-12.00
1 077 ACT 12 1 tuesday 12:00:00
1 077 ACT 12 1 tuesday 12:30:00
1 077 ACT 12 1 tuesday 13:00:00
1 077 ACT 12 1 tuesday 13:30:00
1 077 ACT 12 1 tuesday 14:00:00
1 077 ACT 12 1 tuesday 14:30:00
1 077 ACT 12 1 tuesday 15:00:00
1 077 ACT 12 1 tuesday 15:30:00
1 077 ACT 12 1 tuesday 16:00:00
1 077 ACT 12 1 tuesday 16:30:00
1 077 ACT 12 1 tuesday 17:00:00
1 077 ACT 12 1 tuesday 17:30:00
1 077 ACT 12 1 tuesday 18:00:00
1 077 ACT 12 1 tuesday 18:30:00
1 077 ACT 12 1 tuesday 19:00:00
1 077 ACT 12 1 tuesday 19:30:00
1 077 ACT 12 1 tuesday 20:00:00
1 077 ACT 12 1 tuesday 20:30:00
1 077 ACT 12 1 tuesday 21:00:00
1 077 ACT 12 1 tuesday 21:30:00
1 077 ACT 12 1 tuesday 22:00:00
The table with all possible timeframes does not exist in the database. Can I maybe hadcode it into the query?
You need to create the list of all possible values. You can do this with a query like:
select *
from (select '2012-01-01 7:00:00' from dual union all
select '2012-01-01 7:30:00' from dual union all . . .
) times
That is, you can use union all to put the data together. I find it easiest to use Excel to put together the SQL statements needed to make this work.
That said, a calendar/calendar-time table is something you should consider. Having a table with each possible time slot it in will probably help you in the future.
Take for example this query:
SELECT * FROM Persons WHERE Year='1965'
this sql query will return a data set containing all the rows from the 'Persons' table where all the 'Year' fields equal to '1965'. As you said, you wanted to reverse your sql query so it returns the opposite data set. This is made possible in the WHERE clause, where you can use the not equal to operator: <> or != in some versions of SQL
SELECT * FROM Persons WHERE Year<>'1965'
or
SELECT * FROM Persons WHERE Year!='1965'
will return a data set containing rows from the 'Persons' table where its field does not equal to '1965'.
Hope this helps in your sql query. you simply need to implement the <> or != operator where needed to return the opposite data set from your database this is assuming you wanted the opposite data from the data you're already having returned with your current query.
If this is not what you wanted please update your question so that you clearly explain what output you expect it to have - an example - so that a query can be made to retrieve the desired results.
Let me give you what I think is the answer first:
I think what you are looking for is Between ... And.... Or better to say Not(Between .. And..). Between ... And... is a range operator. So x Between 1 And 10 = 1<x<10 and Not x Between 1 And 10 = x<1 || x>10.
There are certain part of the query that are plain wrong to me:
Why do you have the select * anyway? You could just join the to tables like:
select
rooms.id, rooms.number, rooms.building, rooms.capacity,exam_schedules.room_id,
exam_schedules.day,
exam_schedules.start_time,
exam_schedules.end_time
from
rooms,exam_schedules
where
exam_schedules.room_id = rooms.id and exam_schedules.day = 'tuesday' and rooms.number = '077'
I generally do this with a left join and include only rows that are not in the right table.
SELECT
*
FROM
(select
rooms.id, rooms.number, rooms.building, rooms.capacity
from
rooms) R1
LEFT JOIN (select
exam_schedules.room_id,
exam_schedules.day,
exam_schedules.start_time,
exam_schedules.end_time
from
exam_schedules) R2
ON
R2.room_id = R1.id and R2.day = R1.day and (R2.start BETWEEN R1.start AND R1.end OR R2.end BETWEEN R1.start AND R1.end OR R1.start BETWEEN R2.start AND R2.end OR R1.end BETWEEN R2.start AND R2.end)
WHERE R2.room_id IS NULL;
Related
EDIT: I have added the primary key, following the comment by #Strawberry
The aim is to return the number of current members, and also the number of past memberships, on any particular date/time.
For example, suppose we have
msid id start cancelled
1 1 2020-01-01 09:00:00 null
2 2 2020-01-01 09:00:00 2020-12-31 09:00:00
3 2 2021-01-01 09:00:00 null
4 3 2020-01-01 09:00:00 2020-06-30 09:00:00
5 3 2020-02-01 09:00:00 2020-06-30 09:00:00
6 3 2020-07-01 09:00:00 null
and we want to calculate the number of members at various times, which should return as follows
Datetime Current Past <Notes - not to be returned by the query>
2020-01-01 12:00:00 3 0 -- all 3 IDs have joined earlier on this date
2020-02-01 12:00:00 3 0 -- new membership for existing member (ID 3) is not counted
2020-06-30 12:00:00 2 1 -- ID 3 has cancelled earlier on this day
2020-07-01 12:00:00 3 0 -- ID 3 has re-joined earlier on this day
2020-12-31 12:00:00 2 1 -- ID 2 has cancelled earlier on this day
2021-01-01 12:00:00 3 0 -- ID 2 has re-joined earlier on this day
An ID may either be current or past, but never both. That is, if a past member re-joins, as in the case of ID 2 and 3 above, they become current members, and are no longer past members.
Also, a member may have multiple current memberships, but they can only be counted as a current member once, as in the case of ID 3 above.
How can this be achieved in MySQL ?
Here is a db<>fiddle with the above data
Test this:
WITH
cte1 AS ( SELECT start `timestamp` FROM dt
UNION
SELECT cancelled FROM dt WHERE cancelled IS NOT NULL ),
cte2 AS ( SELECT DISTINCT id
FROM dt )
SELECT cte1.`timestamp`, COUNT(DISTINCT dt.id) current, SUM(dt.id IS NULL) past
FROM cte1
CROSS JOIN cte2
LEFT JOIN dt ON cte1.`timestamp` >= dt.start
AND (cte1.`timestamp` < dt.cancelled OR dt.cancelled IS NULL)
AND cte2.id = dt.id
GROUP BY cte1.`timestamp`
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=942e4c97951ed0929e178134ef67ce69
I have a table where records will be getting inserted every 4 hours on a daily basis. If the record was not inserted for continuous 4 hours, I need to insert a log into another table. Below is the table schema.
Id DocPathid CreatedAt
1 1 2021-04-02 00:00:00
2 1 2021-04-02 04:00:00
3 1 2021-04-02 09:00:00
4 1 2021-04-02 12:00:00
5 1 2021-04-02 16:00:00
6 1 2021-04-02 20:00:00
7 1 2021-04-02 24:00:00
In the above case, there was no records inserted within a interval of 4hours (i.e. between 2021-04-02 04:00:00 & 2021-04-02 09:00:00). The query should return no. of failure count (in this case it is failed for 1 time).
Is there a way to achieve this in MySQL?
You can do something like this.
select count(1)
from (
select id, CreatedAt , timestampdiff(hour, CreatedAt
, lead(CreatedAt,1) over (partition by DocPathid order by CreatedAt) ) as hour
from Table1
) t
where hour >4
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=9b0c631145422dbccd2ea23f0a7d2011
My question is little bit more complicated than what the title implies but here it is:
I have a table with punch data formatted like this:
name time_in time_out location
1 2018-05-31 10:09:00 2018-05-31 16:06:00 1
3 2018-05-31 10:12:00 2018-05-31 17:03:00 1
I would like a select query that returns the total time in hours of people that are working during a 15min time frame for each location. Here is an example output for the two rows given:
time labor_hours location
2018-05-31 10:00:00 .15 1
2018-05-31 10:15:00 .50 1
2018-05-31 10:30:00 .50 1
2018-05-31 10:45:00 .50 1
2018-05-31 11:00:00 .50 1
...
2018-05-31 15:45:00 .50 1
2018-05-31 16:00:00 .35 1
2018-05-31 16:15:00 .25 1
2018-05-31 16:30:00 .25 1
2018-05-31 16:45:00 .25 1
2018-05-31 17:00:00 .10 1
Labor hours is total hours worked during a 15min time period in hours. So for example, the first row was calculated by looking at the first two rows and seeing that from 10:00:00 - 10:15:00 employee 1 and 2 worked for a total of 9 minutes. Since it's in hours 9/60 = .15.
I'm new to sql so I'm pretty lost on how to start with this.
If you are using MySQL 8.0, you can use the CTE feature as follows
WITH cte (timeStamp) AS
(
SELECT "2018-05-31 00:00:00"
UNION ALL
SELECT TIMESTAMPADD(MINUTE, 15, timeStamp)
WHERE timeStamp < 2018-06-01 00:00:00
)
SELECT timeStamp FROM cte;
SELECT cte.time,
sum(TIMESTAMPDIFF(MINUTE, punch.time,
TIMESTAMPAD(MINUTE, 15, cte.timeStamp))) as labour_hours,
punch.location
FROM cte LEFT OUTER JOIN punch ON punch.time >=cte.timeStamp
AND punch.time < TIMESTAMPADD(MINUTE, 15, cte.timeStamp)
GROUP BY punch.location, cte.timeStamp
If you are using an older version of MySQL, you need to create a stored procedure that generates the timestamps with 15 minute intervals.
I have two tables:
Table 1: planA
ID Date Count
3 2017-01-01 10
2 2017-02-03 15
10 2017-01-30 8
Table 2: planB
ID Date Value
3 2017-01-02 11
2 2017-02-04 12
21 2017-01-30 3
3 2017-02-03 33
What I want to do is to join the two tables on (ID and Date) columns.
However, on Date, I want to use the next day to the date on the table 1.
Therefore, the joined table should look like the following:
PlanA.ID PlanA.Date PlanB.Date PlanA.Count PlanB.Value
3 2017-01-01 2017-01-02 10 11
2 2017-02-03 2017-02-04 15 12
Is this even possible?
Any suggestion would be appreciated!
Yes it is possible:
select
PlanA.ID,
PlanA.Date,
PlanB.Date,
PlanA.Count,
PlanB.Value
from
PlanA inner join PlanB
on (
PlanA.ID = PlanB.ID
and
PlanA.Date + INTERVAL 1 DAY = PlanB.Date
)
if Date is a column of type date, + INTERVAL 1 DAY will return the next day of the one given, and then you can perform the join.
I got an alert table for users, in which we have to send alerts to users in user defined intervals like 0 ( only once), 3 months, 6 months, 1 year
So I designed a table like this
id | user_id | alert_date | repeat_int
-----+--------------+-------------------------+-------------
12 | 747 | 2013-04-19 00:00:00 | 0
13 | 746 | 2013-03-19 00:00:00 | 1
14 | 745 | 2012-04-19 00:00:00 | 0
15 | 744 | 2013-04-19 00:00:00 | 0
16 | 743 | 2013-05-19 00:00:00 | 0
We are sending alert just a day before "alert_date"
With the following query I can fetch the data
SELECT al.id,
al.user_id,
al.alert_date,
al.repeat_int AS repunit
FROM alerts AS al
WHERE DATE_ADD(alert_date,INTERVAL repeat_int MONTH)=date_add(CURRENT_DATE,INTERVAL 1 DAY)
OR date(al.alert_date)=date_add(CURRENT_DATE,INTERVAL 1 DAY)
Its working file but my real problem is
The repeat will only works once, we need it repeat every interval
ie. If alert date is 2012-03-14 and repeat_int is 0 - Need to work only once
but if alert date is 2012-03-14 and repeat_int is 1 - Need to work in every 14th from 2012-03-14
and if the alert date is 2012-03-14 and repeat_int is 3 - Need to work in every three month's 14. ie alert on 2012-03-14, 2012-06-14, 2012-09-14 etc...
Is there any way to do that?
Update
The OP has changed his schema in response to comments, so the query is essentially:
SELECT *
FROM alerts
WHERE CURRENT_DATE + INTERVAL 1 DAY = COALESCE(next_alert_date, alert_date);
This handles "next_alert_date" being NULL on the very first run.
Original answer
For the original schema:
SELECT *
FROM alerts
JOIN (SELECT CURRENT_DATE + INTERVAL 1 DAY AS tomorrow) d
WHERE -- We want to alert if
-- 1. Tomorrow is the alert_date
tomorrow = alert_date
OR
--
-- 2. Tomorrow is "repeat_int" months removed from alert_date, falling on
-- the same day of the month or on the end of the month if the original
-- alert_date day of month is later in the month than is possible for us
-- now. E.g., 2013-01-31 repeated monthly is adjusted to 2013-02-28.
(
PERIOD_DIFF(DATE_FORMAT(tomorrow, '%Y%m'), DATE_FORMAT(alert_date, '%Y%m'))
MOD repeat_int = 0
AND
-- Make sure we are at the same day of the month
( (DAYOFMONTH(tomorrow) = DAYOFMONTH(alert_date)
OR
-- Or, if the day of the alert is beyond the last day of our month,
-- that we are at the end of our month.
(LAST_DAY(alert_date) > LAST_DAY(tomorrow)
AND
DAYOFMONTH(tomorrow) = LAST_DAY(tomorrow)) )
);