Matching days of week for a given date range - mysql

I am working on a space booking system and is trying to find out if the space is available for a given date range. So I have 2 tables -
space_master
id space_address start_time end_time
space_availability
space_id days_of_week
Note -
(a) start_time and end_time are DATETIME fields in MYSQL
(b) days_of_week are numbers where Sunday is represented by 1 and so on
(c) one space can be available on multiple days (example follows)
space_master
id space_address start_time end_time
1 Florida 2012-03-18 10:21:00 2012-03-29 4:21:00
2 London 2012-04-21 09:00:00 2012-06-18 10:00:00
space_availability
space_id days_of_week
1 1
1 2
2 4
2 5
2 6
This means the first space (with id 1) is available between 2012-03-18 10:21:00 and 2012-03-29 4:21:00 but only on Sunday and Monday. Now I am trying to write a function that will take booking_start_time and booking_end_time (all DATETIME in MYSQL) as input then scan the available spaces table and return the availble bookings. Something like this -
getBooking(2012-03-19 10:21:00, 2012-03-19 15:21:00) - returns the space with id 1 (as 19th March 2012 was a Monday and hence available)
getBooking(2012-03-19 10:21:00, 2012-03-20 15:21:00) - returns nothing since 20th March is a Tuesday on which the space is not available.
Any idea how to do this? I was first trying to do this in a single query. But is that even possible?
EDIT: the sqlfiddle link follows -
http://sqlfiddle.com/#!9/8a6e1a

Related

get the scheduling data based on 3 years in mysql

I have one table bibles that having two columns only i.e. is below:-
id chapter_name
1 Mathews 1
2 Mathews 2
3 Mathews 3
4 Mathews 4
... ....
... ....
364 Revelation 22 //Total Records only 364 fixed. that will not increase
Bible Scheduling starts from 5 sept 2021 for 3 years and ends on 31 Aug 2024 i.e basically bible repeats three time in 3 years.
Now Suppose today date is 8 Sept 2021
id chapter_name date_1 date_2 date_3
1 Mathews 1 2021-09-05 2022-09-04 2023-09-03
2 Mathews 2 2021-09-06 2022-09-05 2023-09-04
3 Mathews 3 2021-09-07 2022-09-06 2023-09-05
4 Mathews 4 2021-09-08 2022-09-07 2023-09-06
... ....
... ....
So now requested date is 8 spet 2021 i want to fetch the 4th record Mathews 4 and now suppose today date is 6 sept 2022 and now want to fetch the 3 row Mathews 3.
Note : these date columns are not stored in database. i have only two columns id, chapter_name.. Reason for not storing the dates because after three years i need to update again that dates to make scheduling works. Is there any way to fetch the rows?
Below answer works fine but I have two types of Bibles that starts from Id 1 to 364 another starts from 365 to 1456... 1st case
1 to 364 works.. but 365 to 1456 not works
If I understand correctly, then logic you want is:
where dateadiff(?, '2021-09-05') % 364 = id - 1
The ? is a parameter for the date you pass in.
You can start with some anchor date in the past, 2020-09-06 will do.
The id in your table is a number of days between the anchor date and the current date mod 364 (the number of rows in your table).
SELECT
id, chapter_name
FROM bibles
WHERE
id = DATEDIFF(CURRENT_DATE(), '2020-09-06') % 364 + 1
;
The DATEDIFF(CURRENT_DATE(), '2020-09-06') function returns the number of days between the current date and the anchor date 2020-09-06, i.e. the number of days between the date when the schedule started and now. This number can grow larger than 364 (the number of rows in your table). To "wrap" this number back to 0 when it is larger than 364 we use the modulo division (mod) or % operator. And then add 1, because ids in your table start with 1 (if they started with 0 you didn't have to add 1).
Try to run this query replacing the CURRENT_DATE() with your sample dates 2021-09-08 and 2022-09-06.
SELECT DATEDIFF('2021-09-08', '2020-09-06') % 364 + 1;
returns 4 (4th row)
SELECT DATEDIFF('2022-09-06', '2020-09-06') % 364 + 1;
returns 3 (3rd row)
It means that if you run the full query with the CURRENT_DATE() function on 2021-09-08 it will return a row with id=4. When you run the query on 2022-09-06 it will return a row with id=3.

MYSQL How to perform custom month difference between two dates in MYSQL?

My requirement is to compute the total months and then broken months separately between 2 dates (ie first date from table and second date is current date). If broken months total count is > 15 then account it as one month experience and if its les than 15 don't account that as 1 month experience.
Assume I have a date on table as 25/11/2018 and current date is 06/01/2019;
the full month in between is December, so 1 month experience; and broken months are November and January, so now I have to count the dates which is 6 days in Nov and 6 days in Jan, so 12 days and is <= (lte) 15 so total experience will be rounded to 1 month experience
I referred multiple questions related to calculating date difference in MYSQL from stackoverflow, but couldn't find any possible options. The inbuilt functions in MYSQL TIMESTAMPDIFF, TIMEDIFF, PERIOD_DIFF, DATE_DIFF are not giving my required result as their alogrithms are different from my calculation requirement.
Any clue on how to perform this calculation in MYSQL and arrive its result as part of the SQL statement will be helpful to me. Once this value is arrived, in the same SQL, that value will be validated to be within a given value range.
Including sample table structure & value:
table_name = "user"
id | name | join_date
---------------------
1| Sam | 25-11-2017
2| Moe | 03-04-2017
3| Tim | 04-07-2018
4| Sal | 30-01-2017
5| Joe | 13-08-2018
I wanted to find out the users from above table whose experience is calculated in months based on the aforementioned logic. If those months are between either of following ranges, then those users are fetched for further processing.
table_name: "allowed_exp_range"
starting_exp_months | end_exp_months
-------------------------------------
0 | 6
9 | 24
For ex: Sam's experience till date (10-12-2018) based on my calculation is 12+1 month = 13 months. Since 13 is between 9 & 24, Sam's record is one of the expected output.
I think this query will do what you want. It uses
(YEAR(CURDATE())*12+MONTH(CURDATE()))
- (YEAR(STR_TO_DATE(join_date, '%d-%m-%Y'))*12+MONTH(STR_TO_DATE(join_date, '%d-%m-%Y'))) -
- 1
to get the number of whole months of experience for the user,
DAY(LAST_DAY(STR_TO_DATE(join_date, '%d-%m-%Y')))
- DAY(STR_TO_DATE(join_date, '%d-%m-%Y'))
+ 1
to get the number of days in the first month, and
DAY(CURDATE())
to get the number of days in the current month. The two day counts are summed and if the total is > 15, 1 is added to the number of whole months e.g.
SELECT id
, name
, (YEAR(CURDATE())*12+MONTH(CURDATE())) - (YEAR(STR_TO_DATE(join_date, '%d-%m-%Y'))*12+MONTH(STR_TO_DATE(join_date, '%d-%m-%Y'))) - 1 -- whole months
+ CASE WHEN DAY(LAST_DAY(STR_TO_DATE(join_date, '%d-%m-%Y'))) - DAY(STR_TO_DATE(join_date, '%d-%m-%Y')) + 1 + DAY(CURDATE()) > 15 THEN 1 ELSE 0 END -- broken month
AS months
FROM user
We can use this expression as a JOIN condition between user and allowed_exp_range to find all users who have experience within a given range:
SELECT u.id
, u.name
, a.starting_exp_months
, a.end_exp_months
FROM user u
JOIN allowed_exp_range a
ON (YEAR(CURDATE())*12+MONTH(CURDATE())) - (YEAR(STR_TO_DATE(u.join_date, '%d-%m-%Y'))*12+MONTH(STR_TO_DATE(u.join_date, '%d-%m-%Y'))) - 1
+ CASE WHEN DAY(LAST_DAY(STR_TO_DATE(u.join_date, '%d-%m-%Y'))) - DAY(STR_TO_DATE(u.join_date, '%d-%m-%Y')) + 1 + DAY(CURDATE()) > 15 THEN 1 ELSE 0 END
BETWEEN a.starting_exp_months AND a.end_exp_months
Output (for your sample data, includes all users as they all fit into one of the experience ranges):
id name starting_exp_months end_exp_months
1 Sam 9 24
2 Moe 9 24
3 Tim 0 6
4 Sal 9 24
5 Joe 0 6
I've created a small demo on dbfiddle which demonstrates the steps in arriving at the result.

SSRS get sum on an expression field

Suppose I have the following data:
TimeTable
Person Week Date EnterTime ExitTime PeriodDiff
----------------------------------------------------
John 1 01.01.2018 09:15 10:35 1:20
John 1 01.01.2018 10:55 12:23 1:28
John 1 01.01.2018 13:00 17:35 4:35
John 1 02.01.2018 09:00 16:35 7:35
John 2 08.01.2018 09:05 11:40 2:35
John 2 08.01.2018 16:15 19:35 3:20
John 2 09.01.2018 10:50 21:57 11:07
I am trying to make a report about the weekly and daily time people were in the company.
So I am trying to create the following report:
+[WeekGroup] +
---------------------------------------------------------------
+[DateGroup] +
WeeklyTotal ------------------------------------------------
Person FirstEntry LastExit TimeInside Period
[PersonGroup] <<Exp3>> Min[EnterTime] Max[ExitTime] <<Exp1>> <<Exp2>>
Exp1 = Max(ExitTime) - Min(EnterTime)
Exp2 = Sum(PeriodDiff)
Exp3 should be Sum(Max(ExitTime) - Min(EnterTime)) for each day
Everything works except for Exp3.
Problem is Exp3 is outside of the DateGroup so the min/max values refer to each of those in the respected week. Also I cannot use the sum of period because of the gaps in time.
How can I get the sum of each weeks calculated time differance? Meaning sum(Exp1) ?
You can use the following expression to get the Max() or Min() inside the group or outside the group, if you add the scope to the aggregate function:
'Returns the max of the whole DataSet
=Min(Fields!ExitTime.Value), "YourDataSetName")
'Returns the max of the group with the name 'YourGroupName'
=Min(Fields!ExitTime.Value), "YourGroupName")
What you also can do, is to reverence to your first expression Expr1() with the following expression (lets assume the first expression is in Textbox1):
=Sum(ReportItems!Textbox1.Value)

SQL query for various time periods

I have a table that contains Following entries:
completed_time|| BOOK_CNT
*********************************************
2013-07-23 | 2
2013-07-22 | 1
2013-07-19 | 3
2013-07 16 |5
2013-07-12 |4
2013-07-11 |2
2013-07-02 |9
2013-06-30 |5
Now, I want to use above entries for data analysis.
Lets say DAYS_FROM, DAYS_TO and PERIOD are three variables.
I need to fire following sort of queries:
"Total book from DAYS_FROM to DAYS_TO in interval of PERIOD."
DAYS_FROM is a date in format YYYY-MM-DD
,DAYS_TO is a date in format YYYY-MM-DD
PERIOD is {1W,2W,1M,2M,1Y}
where W,M,Y represents WEEK,MONTH and YEAR.
Example: The queries DAYS_FROM=2013-07-23 , DAYS_TO=2013-07-03 and PERIOD=1W should return:
ith week - total
1 - 3
2- 8
3- 6
4- 14
Explanation:
1-3 means (The total book from 2013-07-21(sun) to 2013-07-23(tue) is 3 )
2-8 means (The total book from 2013-07-14(sun) to 2013-07-21(sun) is 8 )
3-16 means (The total book from 2013-07-07(sun) to 2013-07-14(sun) is 6 )
4-14 means (The total book from 2013-07-03(wed) to 2013-07-07(sun) is 14 )
Please refer the calendar image for better understanding.
How to fire such query?
What I tried?
SELECT DAY(completed_time), COUNT(total) AS Total
FROM my_tab
WHERE completed_time BETWEEN '2013-07-23' - INTERVAL 1 WEEK AND '2013-07-03'
GROUP BY DAY(completed_time);
The above queries subtracted 7 days from 2013-07-23 and thus considered 2013-07-16 to 2013-07-23 as first week, 2013-07-09 to 2013-07-16 as second week and so on.
A simple starting point would be something like below, of course you may want to adjust the ith value to suit your needs;
SET #period='1M';
SELECT CASE WHEN #period='1Y' THEN YEAR(completed_time)
WHEN #period='1M' THEN YEAR(completed_time)*100+MONTH(completed_time)
WHEN #period='2M' THEN FLOOR((YEAR(completed_time)*100+MONTH(completed_time))/2)*2
WHEN #period='1W' THEN YEARWEEK(completed_time)
WHEN #period='2W' THEN FLOOR(YEARWEEK(completed_time)/2)*2
END ith,
SUM(BOOK_CNT) Total
FROM my_tab
GROUP BY ith
ORDER BY ith DESC;
An SQLfiddle to test with.

Pair SQL records without Cursor

I am trying to pair records in a SQL table, my table looks similar to this:
UID DATE TIME MateID
---------------------------------------
1 2013-06-07 08:00 NULL
2 2013-06-07 10:00 NULL
3 2013-06-07 13:00 NULL
4 2013-06-07 17:00 NULL
5 2013-06-08 07:00 NULL
6 2013-06-08 11:00 NULL
7 2013-06-08 14:00 NULL
8 2013-06-08 18:00 NULL
I know I can do this with a cursor, but I wanted to know if there was a set based solution that could give me this output:
UID DATE TIME MateID
---------------------------------------
1 2013-06-07 08:00 2
2 2013-06-07 10:00 1
3 2013-06-07 13:00 4
4 2013-06-07 17:00 3
5 2013-06-08 07:00 6
6 2013-06-08 11:00 5
7 2013-06-08 14:00 8
8 2013-06-08 18:00 7
The UID field won't be consecutive, the records will be ordered by DATE and TIME. The table will contain about 50k records
Edit: Sorry I should have been a bit more clear. MateID is the UID of the previous/next record. Records are grouped based on the DATE and ordered by TIME ASC, so the first record and the second record of the DATE are pairs, the third record and fourth record of the DATE are paired too. Please let me know if you need me to explain anything else. There will always be an even number of records per date.
Thanks
You can use ROW_NUMBER() and some simple maths to generate PairIDs:
declare #Tab table (UID int not null,Date date not null,time time not null)
insert into #Tab (UID,Date,Time) values
(1,'20130607','08:00'),
(2,'20130607','10:00'),
(3,'20130607','13:00'),
(4,'20130607','17:00'),
(5,'20130608','07:00'),
(6,'20130608','11:00'),
(7,'20130608','14:00'),
(8,'20130608','18:00')
;With PairedRows as (
select UID,Date,Time,
(ROW_NUMBER() OVER (ORDER BY Date,Time) + 1) / 2 as PairID
from #Tab
)
select p1.UID,p1.Date,p1.Time,p2.UID
from
PairedRows p1
inner join
PairedRows p2
on
p1.PairID = p2.PairID and
p1.UID != p2.UID
(I've done this as a SELECT, but it's easy enough to switch it to an UPDATE if this is meant to be a permanent pairing - it's not really clear from your question)
It may better match your model to PARTITION BY Date and only ORDER BY Time in the ROW_NUMBER() function - but since in this case you've stated that every date has an even number of rows, and all we care about are those rows which are assigned the same PairID without caring about the numeric value, it shouldn't affect the result of the query.
But it may better document your requirements.