MySQL store time conditions - mysql

I am currently working on a platform where I would like to offer some rentals.
I want to store all possible rental types in the database, so I do not need to take care of logic in code.
Basically I want to offer following rental-times:
1 day
7 days
5 days / need to be weekdays ( which is monday to
friday )
2 days / weekend ( needs to be saturday till sunday )
1 month / which will be from e.g. 14 of some month till 13th of following month
For the time of days I thought of storing data in minutes, this is easy to figure out.
If there is a weekday condition I thought of using the DAYOFWEEK Indexes, so I could define for 5 days, the first day of rental needs to have index 2 which is Monday, for weekend I could define that the day of rental needs to have index 6, which is Saturday.
rental_type_id | rental_time_in_minutes | rental_label | rental_start_day_indexes
1 day would be like
1 | 1440 | 1 day | 1,2,3,4,5,6,7
7 days
2 | 10080 | 7 days | 1,2,3,4,5,6,7
5 days
3 | 7200 | Weekdays | 2,3,4,5,6
etc.
Is this a way to go for, or should I store the rental time just in days?
Any help, advice, critics or cheers are helpful!
Thanks.

Depending on how you use this, storing rental time in minutes could cause issues around DST where you have 23 or 25 hours in a day.
Also storing the indices as comma-separated lists will cause you problems. I would make a mapping table. That column is not in normal form and will be tough to query.
I would honestly just make different tables for the different durations.

I would store the durations in seconds. Because it makes the whole thing easier to query and more felxible. Lets say you have those rental durations. Table durations:
d_id type duration
1 1 day 01
2 3 days 03
3 7 days 07
I would suggest that you use normal timestamps for the rentals. I would store the start and end timestamp. Lets say you have the following table.
In this example I'm using days instead of a timestamp just to make it easier to show. Let's say today is day 3. Table rentals:
customer start end
Bob 02 05
Joe 10 11
Ian 03 04
Let's say you want to want to get all the currently active rentals (Bob & Ian). You can fetch it like this:
SELECT * FROM `rentals` WHERE `start`<3 AND 3<`end`
You can find all which of rental type "1 day" (Ian) by using this query:
SELECT * FROM `rentals`,`durations` WHERE `end`-`start`=`duration` and `d_id`=1
The advantage of using timestamps is that you can very precisely set the rental start and end time. You can check how many hours and minutes the customer is too late with giving the object back. Then you can easily calculate the exact additional fees.

Related

Grouping by Month i.e. 30 days from the starting timestamp rather than the actual month

I have a monthly offering that gives customers 30 minutes of use every month and I need to tally up their usage and group it by month, but not sure the best way to handle that since the start date could be any day of the month.
Should I prorate? Convert into full months or count the days?
Is there an ideal way to tally this? Let's say each month they get 30 minutes of use time, so I want to tally how many minutes were used that "month."
Say there is a minutes used table and the start date for the subscription is 2019-06-13 00:00:00
minutes_used_table:
Userid MinutesUsed Date
1 5 2019-06-19
1 6 2019-06-23
1 8 2019-06-28
1 15 2019-07-05
1 3 2019-07-12
1 8 2019-07-19
1 5 2019-08-14
1 3 2019-08-22
1 1 2019-08-26
1 2 2019-09-13
Or, should I prorate it and instead of tracking in 30 day increments, if they start on the 13th of June, should I just count the days from the start date to the end of the month, whatever day that is and then the days from the first of the month until the start date?
Wondering what makes the most sense and how to carry it out?
Use
GROUP BY DATEDIFF(`Date`, #starting_date) DIV #period_length
In your particular case it will be
GROUP BY DATEDIFF(`Date`, '2019-06-13 00:00:00') DIV 30
From coding point of view it's not a big deal to find first date for user, subtract it from dates and count number of 30-days periods.
But this will be very hard to support in future and this solution will have a number of corner cases: for example someone first start to use product at 20 January, after month of usage have been idle for a while and came back in April, 5. What will be right start date for that case?
So I suggest to use calendar months. And may be reduce limit on first month accordingly to the number of days left or give full trial period even on first month as user has ability to spend 30 minutes.

What would be the optimal structure for a fixed availability schedule?

I'm setting up a calendar on which users can indicate when they are available. The calendar is split up in 3 time slots per day, for 7 days a week (monday-morning, monday-afternoon, monday-evening).
I'm a bit stuck on what the best way would be to store this in my database. I've thought about doing:
start_date
end_date
user_id
or
timeslot
user_id
or
user_id&
mon1
mon2
mon3
...
sun3
creating 21 columns doesn't seem optimal, but it does make the availability query very easy.
I would like to keep some flexibility in defining the hours for the slots. The second one appears to give me this, as I can just define the hours per day afterwards.
In the end I have to be able to look up who is available on 2019-02-01 13:00:00 - 2019-02-01 17:15:00. These dates will not correspond with the timeslots.
You can use 3 fields to store this information,
slot_date - to store date of slot like 2018-12-17, 2018-12-18, etc
slot_time - to store time slot like 13:00:00, 15:00:00, etc
user_id - to store user identifier
Let's say your sample data for 2 users with these fields will look somewhat like this,
slot_date slot_time user_id
---------------------------------
2018-12-17 14:00:00 1
2018-12-17 17:00:00 2
You can easily get the availability of the users for a given time slot. For instance, you want to fetch today's availability for afternoon slot ( considering 1 pm to 4 pm is evening slot ), which can be retrieved by following query,
SELECT slot_date, slot_time, user_id
FROM tbl_slot
WHERE slot_date = '2018-12-17'
AND slot_time BETWEEN '13:00:00' AND '15:00:00'
Similarly, you can change the slot_time clause in the query to work for morning and evening slots.
Update
Based on OP's requirement, if a user selects a day and a slot it will be applicable for every month/year. Based on it, you an eliminate the slot_date column and use day_of_week that will store integer values from 1 to 7 i.e. (Sun to Sat)
day_of_week slot_time user_id
-----------------------------------
2 14:00:00 1
2 17:00:00 2
Then, the query to get all afternoon availability will be,
SELECT day_of_week, slot_time, user_id
FROM tbl_slot
WHERE day_of_week = '2'
AND slot_time BETWEEN '13:00:00' AND '15:00:00'

Excel WEEKNUM() vs MySQL YEARWEEK()

I am creating a clock-in time system and so far I have been able to get user clock in time for today and user clock in time for the current week.
The final step is to get user current time for the current pay period.
I have created a list of pay period start & end dates in Excel.
Whenever you use a function like Excel WEEKNUM() or MySQL YEARWEEK(), these functions come with an additional option parameter.
The links below show the differences between these modes in a table.
Excel WEEKNUM() table reference
MySQL YEARWEEK() table reference
My question is, if we do payroll biweekly, which mode do I set in Excel WEEKNUM() that corresponds to MySQL YEARWEEK()?
Attached spreadsheet clock.logic.xlsx
Thank you for any help.
At first the good news: The Excel ISOWEEKNUM function corresponds to the MySQL WEEKOFYEAR which is WEEK(date,3). So determining ISO week numbers is possible.
But all other WEEK modes are simply crap because the definition of the first week in year does not fit any logic used elsewhere. For example, take the simplest mode having Sunday as the first day of the week and the first week of the year is the week, the first day of the year falls in. This is what Excels WEEKNUM function returns with Return_type 1 or omitted. This should be MySQLs WEEK in modus 0 (0-53) or 2 (1-53). But what the heck?
SELECT WEEK('2008-01-01',0); -> 0
SELECT WEEK('2008-01-01',2); -> 52
So MySQL tells us, Tuesday, 2008-01-01, is in week 52 of 2007?
Really? Why?
Because the rule "Week 1 is the first week … with a Sunday in this year" is not fulfilled by MySQL. Instead it seems for MySQL the first week starts with the first Sunday in this year.
So except of the ISO week numbers, all other week numbers from MySQL are wrong. One could think: Let us take modus 0 and simply add 1 to the result. But that fails in 2012. Because there 2012-01-01 is Sunday and there MySQL gives week number 1 in modus 0 as well as in modus 2.
Examples:
Excel:
Date WEEKNUM ISOWEEKNUM
2008-01-01 1 1
2008-02-01 5 5
2008-02-03 6 5
2008-02-04 6 6
2008-12-31 53 1
2009-01-01 1 1
2009-02-01 6 5
2009-12-31 53 53
2012-01-01 1 52
2012-02-01 5 5
2012-12-31 53 1
2016-01-01 1 53
2016-02-01 6 5
2016-12-31 53 52
MySQL:
drop table if exists tmp;
create table tmp (d date);
insert into tmp (d) values
('2008-01-01'),
('2008-02-01'),
('2008-02-03'),
('2008-02-04'),
('2008-12-31'),
('2009-01-01'),
('2009-02-01'),
('2009-12-31'),
('2012-01-01'),
('2012-02-01'),
('2012-12-31'),
('2016-01-01'),
('2016-02-01'),
('2016-12-31');
select d as 'Date', week(d,0), week(d,3) from tmp;
Result:
Date week(d,0) week(d,3)
2008-01-01 0 1
2008-02-01 4 5
2008-02-03 5 5
2008-02-04 5 6
2008-12-31 52 1
2009-01-01 0 1
2009-02-01 5 5
2009-12-31 52 53
2012-01-01 1 52
2012-02-01 5 5
2012-12-31 53 1
2016-01-01 0 53
2016-02-01 5 5
2016-12-31 52 52
If you want to calculate hours in current pay period in Excel, given a two week pay period, then I'd suggest that you don't need week numbers at all (in fact that overcomplicates the calculation, especially at the start or end of the year)
If you have dates in A2:A100 and hours worked on those dates in B2:B100, and a list of pay period start dates in Z2:Z10 then you can get hours in current pay period with this formula
=SUMIF(A2:A100,">="&LOOKUP(TODAY(),Z2:Z10),B2:B100)
I imagine your actual setup is more complicated, but some variation on the above can probably still be used

Finding available timeslots between dates

I am creating a REST API for a booking calendar, and right now I am trying to figure out the most efficient way of writing a query that returns all timestamps between two dates with a 15 minute interval. If I supply2013-09-21 and 2013-09-22 I would like to get:
2013-09-21 00:15:00
2013-09-21 00:30:00
2013-09-21 00:45:00
2013-09-21 01:00:00
2013-09-21 01:15:00
2013-09-21 01:30:00
...
2013-09-22 23:15:00
2013-09-22 23:30:00
2013-09-22 23:45:00
I would then use this query as a subquery and apply some conditions on it to remove timeslots outside working hours (which are not constant), booked timeslots, etc.
I have seen a lot of blog posts where the author creates a "calendar table" which stores all these timestamps, but that seems like a waste to me since that data doesn't need to be stored.
Any suggestions on how I could do this or a better way to fetch/store the data?
Here is a process that generates 95 rows incrementing a date variable as it goes and then left join the table with the dated entries to the "solid" table that has generated dated rows.
select str_to_date('2010-01-01', '%Y-%m-%d') into #ad;
select * from
(select (#ad := date_add(#ad, INTERVAL 15 MINUTE)) as solid_date from wp_posts limit 95) solid
left join
wp_posts
on solid.solid_date = post_date
I've no idea how to generate an arbitrary number of rows in mysql so i'm just selecting from a table with more than 95 rows (24 hours * 4 appointments per hour less one at midnight) -- my wordpress posts table. Nothing stopping you making just such a table and having a single column with a single incrementing integer in if there are no better ways to do it (i'm an oracle guru not a mysql one). Maybe there isn't one: How do I make a row generator in MySQL?
Where you see wp_posts, substitute the name of your appointments table. Where you see the date, substitute your start date.
The query above produces a list of dates starting 15 after midnight on the chosen day (in my example 2010-01-01)
You can add a WHERE appointments.primary_key_column_here IS NULL if you want to find free slots only
Note you didn't include midnight in your spec. If you want midnight on the first day, start the date in the variable 15 minutes before and limit yourself to 96 rows. If you want midnight on the end day, just limit yourself to 96 rows

MySQL: A query to work with timetables?

In the project I'm working on right now the system stores employees' timetables in the table with the following structure:
employee_id | mon_h_s | mon_m_s | mon_h_e | mon_s_e | tue_h_s | tue_m_s | etc.
------------------------------------------------------------------------------
1 06 00 14 30 06 00 ...
2 18 30 07 00 21 00 ...
where:
mon_h_s - monday hours start
mon_m_s - monday minutes start
mon_h_e - monday hours end
mon_m_e - monday minutes end
tue_... - tuesday...
Every day of the week has 4 fields: hours start, minutes start, hours end, minutes end.
So, from the table above we can see that:
employee with the id 1 works from 06:00 to 14:30 on Monday
employee with the id 2 works from 18:30 to 07:00 on Monday (basically, between Monday and Tuesday, at night)
The problem is that I'm not sure how to create a SQL query which takes into account everything including time overlapping (at night time). For example, we need to find an employee who works at 6am (06:00) on Tuesday. In our case both employees (id 1 and id 2) would satisfy this criteria. Employee with the id 1 starts his work at 06:00 on Tuesday, and employee with the id 2 works until 07:00 Tuesday (starts on Monday though).
Any suggestions on how to solve this problem would be greatly appreciated.
Probably something like:
SELECT (1440 + ((mon_h_e*60)+mon_m_e) - ((mon_h_e*60)+mon_m_e)) % 1440
This will give you the time worked in minutes. Basically, it adds 1440 (minutes in a day, or 24h*60min/h) to the difference between end time and start time, and keep the rest (modulo) of 1440.
Now for the design part:
If you can, redesign your table. Your table need not have all days of the week in one row, that will make tallying of weekly times very tedious.
You should consider using real datetimes.
employee_id | entrytime | exittime
1 | 2011-10-31 06:00:00 | 2011-10-31 14:30:00
1 | 2011-11-01 06:00:00 | null
2 | 2011-10-31 18:30:00 | 2011-11-01 07:00:00
2 | 2011-11-01 21:00:00 | null
That way, you have:
Full access to all date and time functions in MySQL
Easy calculation of duration
Easy filtering on incomplete periods
There are four basic cases that you need o handle
A -> when time of lecture starts before given time
B -> when time of lecture starts after given time but falls within range of ending time
C -> when time of lecture starts within given time but ends after
D -> when time of lecture starts before given time and ends after given time
Now, this can be accomplished using simple OR conditions