Selecting time-based data - mysql

What I am trying to achieve is grab data from the database, depending on the time.
For example, I may have multiple prices for an item, and I would like the new price to be effective based on the time and date. So I can schedule price changes in advance.
id link_id datetime price
-------------------------------------------
2 11 2016-11-03 00:00:00 1020
3 11 2016-11-03 01:00:00 1050
4 11 2016-11-03 03:00:00 1090
Let's say the time is 2016-11-03 00:59:00, when a user queries the db they will se the price-1020. But when they query the db a minute later at 2016-11-03 01:00:00 they should get price-1050.
Have tried this WHERE datetime < UTC_TIMESTAMP(), however this does not solve my problem. Also it only needs to select one entry, this selects multiple.
Is there a way MySQLi can do this?

If you are only looking for one item, I would expect something like this:
select p.*
from prices p
where p.item_id = $item_id and -- however you are representing items
p.datetime <= now() -- or UTC Timestamp if that is how the date/time is represented
order by p.datetime desc
limit 1;
I could speculate that "link_id" refers to "items".

Related

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'

How to get all rows where date is in a specific range at a given period?

How can I select all rows from a table where a date column is within a specific range of dates, at a given period (e.g. every 14 days)?
The table has a date column with most every date represented, possibly multiple times. The range is defined by a start date and an end date. The period is a number of days. For example:
Start: 2016-01-01 (friday)
End: 2016-12-31 (saturday)
period: 14 (days)
For the above, the query should return rows for every other Friday in 2016. That is, it should return the rows for the following dates:
2016-01-01
2016-01-15
2016-01-29
2016-02-12
2016-02-26
2016-03-11
2016-03-25
2016-04-08
2016-04-22
2016-05-06
2016-05-20
2016-06-03
2016-06-17
2016-07-01
2016-07-15
2016-07-29
2016-08-12
2016-08-26
2016-09-09
2016-09-23
2016-10-07
2016-10-21
2016-11-04
2016-11-18
2016-12-02
2016-12-16
2016-12-30
Currently, this is done in a stored procedure where a loop fills a temp table with the target dates, which is later joined on. However, I am trying to rewrite this code to step away from stored procedures.
What would be the best way to get the desired rows without using the stored procedure & a temp table? Keep in mind that (one of) the table(s) is quite large at around 1M records indexed on date, so any calculated values might impact the performance severely.
Alternatively, I could calculate all dates in the interval in PHP/RoR and use a massive IN clause, but hopefully there is a better solution.
Try this:
table_name1 is your table
date1 the date field
"2022-01-02" the start (twice included)
"2022-01-10" the end
3 the interval
SELECT date1
FROM table_name1
WHERE date1 BETWEEN "2022-01-02" AND "2022-01-10"
AND (DATE("2022-01-02") - date1) % 3 = 0;
Tested it with MySQL 5.6.

Get peak amount of active rows from all time

I have the following problem. I have a mysql table that has a startdate and and enddate. Each row is considered active between those dates. Some rows are no longer active, but have been active in the past. For example the following table:
id start end
1 2014-11-11 00:00:00 2015-01-31 23:59:59
2 2014-09-25 10:16:14 2015-06-01 23:59:59
3 2013-12-24 00:00:00 2014-12-01 23:59:59
4 2014-08-13 00:00:00 2016-01-31 23:59:59
5 2013-09-11 00:00:00 2014-09-10 23:59:59
My actual table has way more data than that. Now I need to know what the peak amount of concurrent active rows is without knowing when that peak actually occured. How would I do this in SQL? In the example 4 rows are active at the same time (1-4, not 5) in the time between 2014-11-11 and 2015-01-31 23:59:59. The actual peak time doesn't matter to me as much as the peak amount itself.
Thanks for your help
Find different timestamps of interrest using UNION ALL, count number of active tasks at these timestamps:
select ts, (select count(*) from tablename t2
where timestamps.ts between t2.start and t2.end) as count
from (select start as ts
from tablename
union all
select end
from tablename) as timestamps
order by count desc
limit 1
Finally order descending and pick the highest value only!
(From a non MySQL user, so some details may be wrong... Please comment if that's the case and I'll edit!)

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

Generating a MySQL hourly breakdown from current timestamp

I'm afraid this is probably a very embarrassingly easy question - but my mind is just completely stuck at this hour.
I have a table that stores the number of activities carried out by different people, and the time it took place in.
I want to create a report that accepts the person's name as a parameter, and show the number of activities per hour for that person during each of the previous 24 hours starting from current timestamp (now()).
Right now,
SELECT hour(TimeStamp), activities FROM tbl1
WHERE timestamp >= DATE_SUB(NOW(), INTERVAL 24 HOUR) AND Name = ?
GROUP BY hour(timestamp)
only returns to me those hours when any activity was present. However, I want a complete 24 hour breakdown with zero for when there was no activity.
i.e. I'm getting
Hour | No. of Activities
1 | 34
4 | 22
9 | 86
but I want
Hour | No. of Activities
1 | 34
2 | 0
3 | 0
4 | 22
5 | 0
... etc.
How do I do this?
(The order of hours in the example is irrelevant)
You can create a hourly table, and do a LEFT JOIN
create table hourly
(
/* hour is not a reserved keyword */
hour smallint(2) not null default 0
);
insert into hourly values (0),(1).... until 24
SELECT
hourly.hour,
COALESCE(COUNT(activities),0) AS "No of Activities"
FROM hourly
LEFT JOIN tbl1 ON hourly.hour=hour(tbl1.TimeStamp)
WHERE
tbl1.timestamp>=DATE_SUB(NOW(), INTERVAL 24 HOUR) AND
tbl1.Name=?
GROUP BY hourly.hour
ORDER BY hourly.hour;