mysql - Compare current timestamp against two time ranges - mysql

I have a database which holds information about daily functions' opening times.
Usually, the database only has the function ID and the opening times, but here I have the function name as an example.
Table: opening_times
| function_id | function_name | day_open | day_close | eve_open | eve_close |
|-------------|------------------|------------|------------|------------|------------|
| 1 | Charity Function | 1357027200 | 1357056000 | 1357063200 | 1357081200 |
I'd like to write an SQL statement, which sees whether the function has:
Not yet opened for day
Daytime Open
Lunch Break
Evening Open
Closed for day
This will obviously have to compare the current time[stamp] against the two date ranges I have stored in the database.
Could someone help me to achieve this. Once I have the syntax, I will learn!
I can do a standard IF statement, to see whether this is between ONE time-range, but this obviously doesn't achieve the result I need:
Thanks!

Try this::
SELECT
CASE IF UNIX_TIMESTAMP() BETWEEN ot.`day_open` AND ot.`day_close`
THEN 'Within daytime open range'
ELSE 'Not in daytime open range' END as open_status
FROM opening_times ot

SELECT
CASE
WHEN UNIX_TIMESTAMP( ) < ot.`day_open`
THEN 'Function has not started'
WHEN UNIX_TIMESTAMP() BETWEEN ot.`day_open` AND ot.`day_close`
THEN 'function open'
WHEN UNIX_TIMESTAMP() BETWEEN ot.`day_close` AND ot.`eve_open`
THEN 'Mid-Day Break'
WHEN UNIX_TIMESTAMP() BETWEEN ot.`eve_open` AND ot.`eve_close`
THEN 'Evening Open'
WHEN UNIX_TIMESTAMP() > ot.`eve_close`
THEN 'Closed for day'
END AS function_open_status
FROM opening_times ot WHERE ot.function_id=1

Related

mysql, getting time period between two time's when date and time are in separated column

I need to query the info in MySql where I'm given two time strings, so I need to find anything in between.
the format the table looks like
id | date | hour | other | columns | that are not important
-----------------------------------------------------------
1 | 2016-04-11| 1 | asdsa......
2 | 2016-04-11| 2 | asdasdsadsadas...
.
.
.
n | 2016-04-12| 23 | sadasdsadsadasd
Say I have the time strings 2016-04-11 1 and 2016-04-12 23 and I need to find all info from 1 to n. I can separate the date and hour and do a query using BETWEEN...AND for the date, but I have no idea how to fit the time into the formula. Using another BETWEEN definitely won't work, so I definitely need to fit the statement somewhere else. I'm not sure how to proceed though.
WHERE ((`date` = fromDate AND `hour` > fromHour) OR `date` > fromDate)
AND ((`date` = toDate AND `hour` < toHour) OR `date` < toDate)

Is it possible to use the current dayname in the where clause in MySQL?

I am trying to create a view in MySQL based on the current day of the week. I am creating a table to keep track of tasks based on the day of the week. For example, some tasks will happen every Tuesday, some will happen on Wednesday and Friday, etc.
I decided to set the table up with a column for each day of the week. If the task needs to be executed on that day I will store a 1 in the column, otherwise it will be a 0. The table looks like this:
| ID | Monday | Tuesday | Wednesday | Thursday | Friday | Task |
-----------------------------------
| 1 | 0 | 1 | 0 | 0 | 0 | "SomeTask" |
| 2 | 0 | 0 | 1 | 0 | 1 | "SomeTask" |
| 3 | 0 | 1 | 0 | 0 | 0 | "SomeTask" |
I would like to create a SELECT statement that will be used in a view to show the tasks that need to be executed on the current day. In other words, today is Tuesday so I would like to a query that will get the rows with the ID of 1 and 3 to show up.
I tried the following , but it didn't work:
SELECT * FROM MyTasks WHERE DAYNAME(curdate()) = 1
Is there a better way to format the table? Is there anyway to use DAYNAME in the WHERE clause? Any suggestions?
You can use case like this:
SELECT * FROM `MyTasks` WHERE (CASE DAYNAME(NOW())
WHEN 'Monday' THEN `Monday`=1
WHEN 'Tuesday' THEN `Tuesday`=1
WHEN 'Wednesday' THEN `Wednesday`=1
WHEN 'Thursday' THEN `Thursday`=1
WHEN 'Friday' THEN `Friday`=1
END)
Apart from that I don't see any way of you accomplishing this, as the column names are static and can't be dynamically built up based on other functions etc
you can get day name of using DAYNAME(curdate()) function
this is returning Thursday (today is 2015-03-05) but,
According to your table structure have to use 1 of following queries
01 SELECT * FROM MyTasks WHERE (
CASE DAYNAME(curdate())
WHEN 'Monday' THEN `Monday`=1
WHEN 'Tuesday' THEN `Tuesday`=1
WHEN 'Wednesday' THEN `Wednesday`=1
WHEN 'Thursday' THEN `Thursday`=1
WHEN 'Friday' THEN `Friday`=1
END)
02 SELECT * FROM MyTasks WHERE (
CASE weekday(curdate())
WHEN 0 THEN `Monday`=1
WHEN 1 THEN `Tuesday`=1
WHEN 2 THEN `Wednesday`=1
WHEN 3 THEN `Thursday`=1
WHEN 4 THEN `Friday`=1
END)
DAYNAME returns you Name of Day in a week, so your query should be:
SELECT * FROM MyTasks WHERE DAYNAME(NOW()) = 'Saturday';
I think you need DAYOFWEEK function to get week day index.
Use the WEEKDAY() function to get the answer what you are looking for.
SELECT * FROM MyTasks WHERE WEEKDAY(curdate()) = 1
It would be better to define a column named Day that would be an enum of each day of the week, instead of the 7 columns you have, like this :
`Day ENUM(1, 2, 3, 4, 5, 6, 7)`
You can then just convert the current day into the adequate value (e.g. from 1 to 7) and use it in your SQL query, like this using PHP :
$sql = 'select * from table where Day = ' . date('N');
date('N') will return a value from 1 to 7 depending on the current day of the week.
Note : this will use the server time of the machine running PHP.
Here is an example of the table :
CREATE TABLE IF NOT EXISTS `enumtest` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`Day` enum('1','2','3','4','5','6','7') NOT NULL,
`Task` varchar(100) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
You can keep your model as it is and use one of two solutions: interpolate the column name on your language as you do with column values (hacky) or you can use an stored procedure for that.
But you can also do this in a RDBMS usual way, with two tables and a joint one. You would have a weekday, a task and a weekday_task table in your schema.
Table task would only have data related to the task itself, maybe with a surrogate serial id. Table weekday only data related to the weekday itself, nothing much, just information as its name and probably an working_day attribute.
And the joint would just include the task Pk and the weekday Pk. It is an ordinary n:m relation and the record would exist only for appointments.
This model is probably missing a lot of stuff related to the usual domain problem and appears to be a learning exercise, so, if it is about learning, you should go with the n:m solution.
The actual problem will probably grow to require by (valid times, start and end) for both task and weekday_task, as the weekday will probably gain a specific day companion, as a more complex solution to deal with real world frequencies anyway. This is not trivial stuff and may be GoF already mapped this for you both as persistence model and domain model.

Graph per-day from ranges in MySQL

I am trying to make a graph that has a point for each day showing the number of horses present per-day.
This is example of data I have (MySQL)
horse_id | start_date | end_date |
1 | 2011-04-02 | 2011-04-03 |
2 | 2011-04-02 | NULL |
3 | 2011-04-04 | 2014-07-20 |
4 | 2012-05-11 | NULL
So a graph on that data should output one row per day starting on 2011-04-02 and ending on CURDATE, for each day it should return how many horses are registered.
I can't quite wrap my head around how I would do this, since I only have a start date and an end date for each item, and I want to know per-day how many was present on that day.
Right now, I do a loop and a SQL query per day, but that is - as you might have guesses - thousands of queries, and I was hoping it could be done smarter.
If a day between 2011-04-02 and now contains nothing, I still want it out but with a 0.
If possible I would like to avoid having a table with a row for each day containing a count.
I hope it makes sense, I am very stuck here.
What you should have, is a table containing just dates from at least the earliest date in your current table till the current date.
Then you can use this table to left join it something like this:
SELECT
dt.date,
COUNT(yt.horse_id)
FROM
dates_table dt
LEFT JOIN your_table yt ON dt.date BETWEEN yt.start_date AND COALESCE(end_date, CURDATE())
GROUP BY dt.date
Be sure to have a column of your_table in the COUNT() function, otherwise it counts the NULL values too.
The COALESCE() function returns the first of its parameter which isn't NULL, so if you don't have an end_date specified, the current date is taken instead.

Get only latest date from database (but get all rows with that latest date)

I'm creating a small application that displays openinghours.
It works like this: I generate a list of dates, then check the database if we are open or not.
The database normal contains:
day_of_week | open | close | since
INT(11) | TIME | TIME | DATE
------------|--------|--------|------------
1 |09:00:00|17:00:00|2013-01-09
2 |08:00:00|18:00:00|2014-01-06
...
The since-date show since what date the openinghours are used (so I can look back in the past and get the right hours, as well as in the future.)
In my list of date I check if since >= $date_in_list and only display max value:
SELECT *,MAX(since) FROM normal WHERE day_of_week = '2' AND since <= '2014-06-03'
This works fine, unless I have 2 hours with the same since-date (e.g. when we close at noon.)
So when I have data like this:
day_of_week | open | close | since
------------|--------|--------|------------
2 |09:00:00|12:00:00|2014-01-06
2 |14:00:00|18:00:00|2014-01-06
I would like to get both 09:00:00 - 12:00:00 and 14:00:00 - 18:00:00, but I can't seem to find the right query.
How about using a subquery? Something along these lines
SELECT *
FROM normal
WHERE day_of_week = '2'
AND since = (
SELECT MAX(since)
FROM normal
WHERE day_of_week = '2'
)
Try with GROUP BY clause like this
SELECT *,
MAX(since)
FROM normal
WHERE day_of_week = '2'
AND since <= '2014-06-03'
GROUP BY `since`,`open`,`close`;

SQL - Reduce the time request

I've got this table (eod) :
| eod_id | company_symbol | date | open | close | high | low |
| 1 | AAA | 01-01-2000 | 40.00 | 42.00 | 43.00 | 39.00 |
I use those 3 requests :
1. SELECT COUNT(*) FROM eod WHERE company_symbol="AAA" AND CLOSE>OPEN
AND DATE BETWEEN "0000-00-00" AND "0000-00-00";
2. SELECT COUNT(*) FROM eod WHERE company_symbol="AAA" AND CLOSE<OPEN
AND DATE BETWEEN "0000-00-00" AND "0000-00-00";
3. SELECT min(date), max(date) FROM eod WHERE company_symbol="AAA"
Each request takes around 0,7sec to be executed, so I would like to reduce the time of each one. How could I process ? Is it possible to do the two first requests in only one ?
Thanks in advance for your help,
Steve
Combining first two:
SELECT
SUM(CASE WHEN CLOSE>OPEN THEN 1 ELSE 0 END) as Higher,
SUM(CASE WHEN CLOSE<OPEN THEN 1 ELSE 0 END) as Lower
FROM eod WHERE company_symbol="AAA"
AND DATE BETWEEN "0000-00-00" AND "0000-00-00";
As you suspect the first two queries can be combined:
SELECT COUNT(CASE WHEN CLOSE < OPEN THEN 1 END),
COUNT(CASE WHEN CLOSE > OPEN THEN 1 END)
FROM eod
WHERE company_symbol="AAA"
AND DATE BETWEEN "0000-00-00" AND "0000-00-00";
(I assume you DATE BETWEEN clause is just an example, but if not it could be changed to DATE = "0000-00-00")
I'd say a nonclustered index on company_symbol is a must, if your dbms supports non key columns then include OPEN, CLOSE and DATE in this index, then depending on how frequently you insert/update data it may also be worth having indexes on your date columns too.
As always with performance based questions you are in a much better position to help yourself than we are to help you, you can view execution plans, IO statistics, and run various tests etc to determine what is slowing your query down, once you have identified the problem more specifically you can then look at adding specific indexes to resolve the problem.
My first suggestion to improve performance would be to never ever user count(*) but to use a single column like count(eod_id) in your case.