Select users who fits in timetable mysql - mysql

I need to select all users who "fits" in theirs working timetables.
Table in MySQL
Timetable:
user_id PRIMARY
day_number(0-sunday 6-saturday) PRIMARY (one user - one day)
start start worktime
end end worktime
Sample user:
user_id = 1
day_number = 1
start = 10:00
end = 18:00
user_id = 1
day_number = 2
start = 12:00
end = 18:00
user_id = 1
day_number = 3
start = 14:00
end = 16:00
Now I want select every user who works from 1(Monday) to 3(Wednesday) from 14:00 to 16:00.
Sample user (with id 1) should be included.
Any Ideas?

SELECT user_id
FROM my_table
NATURAL JOIN (SELECT 1 day_number UNION ALL SELECT 2 UNION ALL SELECT 3) days
JOIN (SELECT MAKETIME(14,0,0) start, MAKETIME(16,0,0) end) times
ON my_table.start <= times.start
AND my_table.end >= times.end
GROUP BY user_id
HAVING COUNT(DISTINCT my_table.day_number) = 3 -- number of days in range
See it on sqlfiddle.

Try this query
SELECT *
FROM `Timetable`
WHERE day_number between
1 and 3
AND TIME_TO_SEC( start ) <= TIME_TO_SEC( '14:00' )
AND TIME_TO_SEC( end ) >= TIME_TO_SEC( '16:00' )
Or
SELECT *
FROM `Timetable`
WHERE day_number in (1,2,3 )
AND TIME_TO_SEC( start ) <= TIME_TO_SEC( '14:00' )
AND TIME_TO_SEC( end ) >= TIME_TO_SEC( '16:00' )

Try the code below:
Select * from Timetable
Where day_number>=1 and day_number <=3 And
hour(start) >= 12 and hour(end)<= 16

Related

MYSQL Subtracting two SELECT Queries

Using MYSQL, I have written two big SELECT queries combined by a UNION, to get 2 rows, where the first row is the count for the current month, and the second row is the count for the previous month. The Query is as follows:
select * from
(select count(*) as type1 from table_x where nationality_id = 23 and month(START_DATE) = month(now())) as t1,
(select count(*) as type2 from table_x where nationality_id = 24 and month(START_DATE) = month(now())) as t2,
(select count(*) as type3 from table_x where nationality_id = 25 and month(START_DATE) = month(now())) as t3,
(select count(*) as type4 from table_x where nationality_id = 26 and month(START_DATE) = month(now())) as t4
UNION
select * from
(select count(*) as type1 from table_x where nationality_id = 23 and month(START_DATE) = month(now() - INTERVAL 1 MONTH)) as t1,
(select count(*) as type2 from table_x where nationality_id = 24 and month(START_DATE) = month(now() - INTERVAL 1 MONTH)) as t2,
(select count(*) as type3 from table_x where nationality_id = 25 and month(START_DATE) = month(now() - INTERVAL 1 MONTH)) as t3,
(select count(*) as type4 from table_x where nationality_id = 26 and month(START_DATE) = month(now() - INTERVAL 1 MONTH)) as t4
I want to add a third row, which is the difference between row 2 and row 1.
How can I do this with my current query?
You are obviously doing a compare between current and prior month. So, I would start with my inner pre-query aggregate getting only those transactions >= the first of the prior month AND the records within the nationality IDs you are looking for.
The inner date_sub() of DAYOFMONTH() -1 day gives you the first of the CURRENT month. By subtracting one more month, gives you the first of the LAST month.
Now you can aggregate the totals per nationality compared to current month or not. That inner query gives you all begin and end counts. Now that is wrapped to the outer and you can get all the counts in addition to the differences... Obviously you can change the column names respectively.
select
PQ.*,
PQ.ThisMonth23 - PQ.LastMonth23 = Diff23,
PQ.ThisMonth24 - PQ.LastMonth24 = Diff24,
PQ.ThisMonth25 - PQ.LastMonth25 = Diff25,
PQ.ThisMonth26 - PQ.LastMonth26 = Diff26
from
( select
sum( case when t.Nationality_id = 23 and month( t.StartDate ) = month( now()) then 1 else 0 end ) ThisMonth23,
sum( case when t.Nationality_id = 24 and month( t.StartDate ) = month( now()) then 1 else 0 end ) ThisMonth24,
sum( case when t.Nationality_id = 25 and month( t.StartDate ) = month( now()) then 1 else 0 end ) ThisMonth25,
sum( case when t.Nationality_id = 26 and month( t.StartDate ) = month( now()) then 1 else 0 end ) ThisMonth26,
sum( case when t.Nationality_id = 23 and month( t.StartDate ) != month( now()) then 1 else 0 end ) LastMonth23,
sum( case when t.Nationality_id = 24 and month( t.StartDate ) != month( now()) then 1 else 0 end ) LastMonth24,
sum( case when t.Nationality_id = 25 and month( t.StartDate ) != month( now()) then 1 else 0 end ) LastMonth25,
sum( case when t.Nationality_id = 26 and month( t.StartDate ) != month( now()) then 1 else 0 end ) LastMonth26
from
table_x t
where
t.StartDate >= date_sub( date_sub( t.StartDate, interval DAYOFMONTH( t.StartDate ) -1 DAY ), interval 1 MONTH )
AND t.Nationality_id IN ( 23, 24, 25, 26 )
) PQ
I would just add that your query might be getting more than you think... You are asking for ALL records Ex: January REGARDLESS of the year, compared to ALL records December REGARDLESS of the year because all you are qualifying is based on the MONTH() and no YEAR() consideration. I am explicitly querying back only current and prior month.

SQL generate free slots on the fly from bookings table?

I know there's a lot of booking questions on php section.
Belive me when i tell you that I tried most of them, at least those which seems compatible. So let me explain.
I have this appointments table
ID | day | start | end |
----------------------------------
1 | 01-01-2018 | 09:00 | 10:00 |
2 | 01-01-2018 | 10:00 | 13:00 |
3 | 02-01-2018 | 12:00 | 15:00 |
4 | 02-01-2018 | 18:00 | 19:30 |
I was wondering, it possibile with sql to get empty time slots? The result should like something like:
day | start | end
---------------------------
01-01-2018 | 00:00 | 09:00
01-01-2018 | 13:00 | 23:59
02-01-2018 | 00:00 | 12:00
02-01-2018 | 15:00 | 18:00
02-01-2018 | 19:30 | 23:59
The query should contain 2 dates: start_day + end_day
I prepared a fiddle here: https://www.db-fiddle.com/f/6dm8q8UtmDkkkjExYfMEbx/1
MSSQL Version
WITH X AS
(
SELECT ROW_NUMBER() OVER (ORDER BY Day, Start)sq, [Day], [Start], [End]
FROM (
SELECT [Day], [Start], [End]
FROM [appointments]
UNION
SELECT Day, '00:00', '00:00'
FROM [appointments]
UNION
SELECT Day, '23:59', '23:59'
FROM [appointments]
) T1
)
SELECT A.Day, A.[End] AS Start, b.[Start] AS End
FROM x A
JOIN x B
ON A.sq = B.sq -1
AND A.[Day] = B.[Day]
AND A.[End] <> b.[Start]
Mysql 5.7 version
SET #RowNumber = 0;
CREATE TABLE cte
SELECT (#RowNumber := #RowNumber+1) AS Rownumber, Day, Start, End
FROM (
SELECT Day, Start, End
FROM booking
UNION
SELECT Day, '00:00', '00:00'
FROM booking
UNION
SELECT Day, '23:59', '23:59'
FROM booking
) T1
ORDER BY day ASC, Start ASC
;
SELECT A.Day, A.End AS Start, B.Start AS End
FROM cte A
JOIN cte B
ON A.Rownumber = B.Rownumber -1
AND A.Day = B.Day
AND A.End <> B.Start
ORDER BY A.Day asc, A.End asc
Will add a fiddle to demonstrate
https://www.db-fiddle.com/f/6dm8q8UtmDkkkjExYfMEbx/2
Mysql 5.7 including days without bookings
SET #RowNumber = 0;
CREATE TABLE cte
SELECT (#RowNumber := #RowNumber+1) AS Rownumber, Day, Start, End
FROM (
SELECT Day, Start, End
FROM booking
UNION
SELECT Day, '00:00', '00:00'
FROM booking
UNION
SELECT Day, '23:59', '23:59'
FROM booking
) T1
ORDER BY day ASC, Start ASC
;
SELECT DAY, Start, End
FROM(
SELECT A.Day, A.End AS Start, B.Start AS End
FROM cte A
JOIN cte B
ON A.Rownumber = B.Rownumber -1
AND A.Day = B.Day
AND A.End <> B.Start
UNION
SELECT DATE_ADD(A.Day, INTERVAL 1 DAY) AS Day, B.Start AS Start, A.End AS End
FROM cte A
JOIN cte B
ON A.Rownumber = B.Rownumber -1
AND A.Day <> B.Day
)Result
ORDER BY Day ASC, Start ASC
https://www.db-fiddle.com/f/6dm8q8UtmDkkkjExYfMEbx/3
Here is a solution below. Assumed the logic of ID column is identity(1,1), otherwise please generate row_number() for each column first. Syntax might not match with MySql, but you will get the logic and apply same if u like.
CREATE TABLE #result (
[day] date NOT NULL,
[start] time NOT NULL,
[end] time NOT NULL
)
declare #maxid int = (select max(id) from #booking), #counter int = 1, #day date
declare #tempStart time = '00:00', #currentStart time ='00:00'
declare #tempDay date = (select TOP 1 [day] from #booking)
while #counter <= #maxid
begin
set #currentStart = (select start from #booking where id=#counter)
set #day = (select [day] from #booking where id=#counter)
if (#day > DATEADD(day,1,#tempDay))
begin
insert into #result values (DATEADD(day,1,#tempDay),'00:00', '23:00')
set #tempDay = #day
end
if(#tempStart < #currentStart)
begin
insert into #result values (#day, #tempStart, #currentStart)
end
if(#counter = #maxid and #tempStart <> '23:59')
begin
insert into #result values (#day, (select [end] from #booking where id=#counter), '23:59')
end
set #tempStart = (select [end] from #booking where id=#counter)
set #counter = #counter + 1
end
select * from #result
Example for SQL Server: https://rextester.com/live/VBNJ34000

sql query to get conflicts on time like an alarm

I’m trying to create an sql query to get a list of possible conflicts on my table but so far my sql doesn’t work.
The idea is to have like an alarm function
If date_start = date_end that means one shot
Example: date_start = 2018-11-07 10:37:00 and date_end = 2018-11-07 10:37:00
If date_start not null and date _end = null , one of the days column should be different from 0
Example: date_start = 2018-11-07 10:37:00 and date_end = NULL and Monday = 2
.
That means starting the start_date every Monday I’m gonna get the row at that time
My problem now is that I’m trying to get a list of conflicts if I have 2 rows that can be at the same time and date . Here is an example of my database:
I created an sql query above but It’s not working well, any one can help ?
SELECT
id
FROM
my_table
WHERE
date_start IN(
SELECT
date_start
FROM
my_table
WHERE
date_start = date_end
GROUP BY
date_start
HAVING
COUNT(*) > 1
)
UNION
SELECT
id
FROM
my_table
WHERE
DATE_FORMAT(date_start, '%H:%i:%s') IN(
SELECT
DATE_FORMAT(date_start, '%H:%i:%s')
FROM
my_table
WHERE
date_end IS NULL AND(
monday = 2 OR tuesday = 3 OR wednesday = 4 OR thursday = 5 OR friday = 6 OR saturday = 7 OR sunday = 1
)
GROUP BY
DATE_FORMAT(date_start, '%H:%i:%s')
HAVING
COUNT(*) > 1
)
UNION
SELECT
id
FROM
my_table
WHERE
DATE_FORMAT(date_start, '%H:%i:%s') IN(
SELECT
CASE WHEN
(DAYOFWEEK(date_start) = 2 AND (select monday from my_table where monday = 2) IS NOT NULL)
OR (DAYOFWEEK(date_start) = 3 AND (select tuesday from my_table where tuesday = 3) IS NOT NULL)
OR (DAYOFWEEK(date_start) = 4 AND (select wednesday from my_table where wednesday = 4) IS NOT NULL)
OR (DAYOFWEEK(date_start) = 5 AND (select thursday from my_table where thursday = 5) IS NOT NULL)
OR (DAYOFWEEK(date_start) = 6 AND (select friday from my_table where friday = 6) IS NOT NULL)
OR (DAYOFWEEK(date_start) = 7 AND (select saturday from my_table where saturday = 7) IS NOT NULL)
OR (DAYOFWEEK(date_start) = 1 AND (select sunday from my_table where sunday = 1) IS NOT NULL)
THEN DATE_FORMAT(date_start, '%H:%i:%s')
ELSE NULL
END
FROM
my_table
WHERE
(DAYOFWEEK(date_start) = 2
OR DAYOFWEEK(date_start) = 3
OR DAYOFWEEK(date_start) = 4
OR DAYOFWEEK(date_start) = 5
OR DAYOFWEEK(date_start) = 6
OR DAYOFWEEK(date_start) = 7
OR DAYOFWEEK(date_start) = 1
)
GROUP BY
DATE_FORMAT(date_start, '%H:%i:%s')
HAVING
COUNT(*) > 1
)
Perhaps something like this
SELECT date_start FROM alarms
WHERE date_end IS NULL AND
monday + tuesday + wednesday + thursday + friday + saturday + sunday > 0
GROUP BY TIME(date_start)
HAVING COUNT(monday)>0 OR COUNT(tuesday)>0 OR COUNT(wednesday)>0 OR
COUNT(thursday)>0 OR COUNT(friday)>0 OR COUNT(saturday)>0 OR COUNT(sunday)>0
UPDATE
If you want to catch conflicts between one-shot events and weekly events you can use a query like this
SELECT a1.date_start AS oneshow,a2.date_start AS weekly,DAYOFWEEK(a1.date_start) as dow
FROM alarms AS a1
LEFT JOIN alarms AS a2 ON
a1.date_start >= a2.date_start AND a2.date_end IS NULL
AND TIME(a1.date_start) = TIME(a2.date_start)
AND DAYOFWEEK(a1.date_start) IN (a2.monday, a2.tuesday, a2.wednesday, a2.thursday, a2.friday, a2.saturday, a2.sunday)
WHERE a1.date_end IS NOT NULL

How to change format of the MySQL result?

I have a complex mysql query language, including several sub queries and my final result is as below. There is something that I am dealing with it and I can't solve it and this is a way result is being presented. I am wondering to know how can i change the structure of the result in a way that the result is being presented only in one row and I don't want to see NULL fields. I mean something like below
This is mysql query
select count(*) as userRetentionSameDay, null as 'userRetentionDiffDay' from (SELECT date(`timestamp`), `user_xmpp_login`
FROM table1
WHERE DATE(`timestamp` ) = CURDATE() - INTERVAL 1 DAY) as res1
right join (select date(ts), user
from table2
WHERE DATE(ts ) = CURDATE() - INTERVAL 1 DAY
and product_id REGEXP ("^(europe+$" )) as lej1
on lej1.user = res1.`user_xmpp_login`
where res1.`user_xmpp_login` IS not NULL
union all
select null as 'userRetentionSameDay', count(*) as userRetentionDiffDay from (SELECT date(`timestamp`), `user_xmpp_login`
FROM table1
WHERE DATE(`timestamp` ) = CURDATE() - INTERVAL 1 DAY) as res1
right join (select date(ts), user
from table2
WHERE DATE(ts ) = CURDATE() - INTERVAL 1 DAY
and product_id REGEXP ("^(europe+$" )) as lej2
on lej2.user = res1.`user_xmpp_login`
where res1.`user_xmpp_login` IS NULL;
What are the recommended solutions to doing that?
try this.
SELECT A.userRetentionSameDay,B.userRetentionDiffDay FROM (
SELECT COUNT() AS userRetentionSameDay FROM
(
SELECT DATE(timestamp), user_xmpp_login
FROM table1
WHERE DATE(timestamp ) = CURDATE() - INTERVAL 1 DAY) AS res1
RIGHT JOIN (SELECT DATE(ts), USER
FROM table2
WHERE DATE(ts ) = CURDATE() - INTERVAL 1 DAY
AND product_id REGEXP ("^(europe+$" )) AS lej1
ON lej1.user = res1.user_xmpp_login
WHERE res1.user_xmpp_login IS NOT NULL
) A,
(
SELECT COUNT() AS userRetentionDiffDay FROM (
SELECT DATE(timestamp), user_xmpp_login
FROM table1
WHERE DATE(timestamp ) = CURDATE() - INTERVAL 1 DAY
) AS res1
RIGHT JOIN (SELECT DATE(ts), USER
FROM table2
WHERE DATE(ts ) = CURDATE() - INTERVAL 1 DAY
AND product_id REGEXP ("^(europe+$" )
) AS lej2
ON lej2.user = res1.user_xmpp_login
WHERE res1.user_xmpp_login IS NULL
) B;

Get the 0 value if No result in column -- MySQL

I have the following query
SELECT COUNT( iContactId ) AS Users, DATE( dCreatedAt ) AS ActivityDate
FROM contacts WHERE iAppId =".$iAppId."
AND DATE(dCreatedAt) IN (CURRENT_DATE(), CURRENT_DATE()-1 )
GROUP BY ActivityDate
by this i am getting
Users |ActivityDate
1 |2014-09-19
i want 0 if there is no match rows
e.g
Users |ActivityDate
0 |CURRENT_DATE()
0 | CURRENT_DATE()-1
How can i do that.
QUERY
SELECT
COUNT(C.iContactId) AS Users,
DATE(C.dCreatedAt) AS ActivityDate
FROM
contacts C
LEFT OUTER JOIN
(
SELECT CURRENT_DATE() AS Dates FROM dual
UNION
SELECT CURRENT_DATE() - 1 AS Dates FROM dual
) D
ON
D.Dates = DATE(C.dCreatedAt)
WHERE
C.iAppId =".$iAppId."
GROUP BY
C.ActivityDate
You could use union
SELECT COUNT( iContactId ) AS Users, CURRENT_DATE() AS ActivityDate
FROM contacts WHERE iAppId =".$iAppId."
AND DATE(dCreatedAt) = CURRENT_DATE()
UNION
SELECT COUNT( iContactId ) AS Users, CURRENT_DATE() - 1 AS ActivityDate
FROM contacts WHERE iAppId =".$iAppId."
AND DATE(dCreatedAt) = CURRENT_DATE() - 1
QUERY
SELECT
ISNULL( iContactId ,0) AS Users,
DATE( dCreatedAt ) AS ActivityDate
FROM contacts
WHERE iAppId =".$iAppId."
AND DATE(dCreatedAt) IN (CURRENT_DATE(), CURRENT_DATE()-1 )
GROUP BY ActivityDate
In most answers this is used voor date substraction:
SELECT CURRENT_DATE() - 1
-> 20140919
This will result in impliciet type casting and date wil be casted to int, use this instead:
SELECT CURRENT_DATE - INTERVAL 1 DAY
-> 2014-09-19
My solution
SELECT
d.ActivityDate,
COUNT(c.iContactId) Users
FROM
(
SELECT CURRENT_DATE - INTERVAL 1 DAY ActivityDate FROM DUAL
UNION
SELECT CURRENT_DATE FROM DUAL
) d
LEFT JOIN
contacts c
ON DATE(c.dCreatedAt) = d.ActivityDate
AND c.iAppId = " . $iAppId . "
GROUP BY d.ActivityDate