Store multiple ranges in mysql table to make queries faster/easier - mysql

I have a data with multiple time ranges, for e.g. consider following columns
| from1 | to1 | from2 | to2 | from3 | to3 |
| 06:00 | 07:30 | 09:30 | 12:30 | 13:30 | 15:45 |
| 05:00 | 06:30 | 08:15 | 14:40 | 16:30 | 18:25 |
Now if I want to search for a time say 08:30, I would have to add 3 clauses in the query to match if the input occurs in range any out of all three from-to pairs.
In above case, it would return second row as 08:30 lies in the second from-to pair.
I want to know what would be the best practice to do this? It is ok even if I have to change my data model and not store those ranges in columns like I shown above. so that I can quickly and easily search through thousands of records
I can't think of a better alternative to this, please suggest.

I found this when i was searching for this problem.
It seems like your data model is not normalized. You should consider morjas suggestion about creating an additional table.
Below is a really ugly query that checks whether a date is in any of the three ranges, and then returns the matching rate.
select case
when date '2010-12-05' between range1_from and range1_to then range1_rate
when date '2010-12-05' between range2_from and range2_to then range2_rate
when date '2010-12-05' between range3_from and range3_to then range3_rate
end as rate
from events
where date '2010-12-05' between range1_from and range1_to
or date '2010-12-05' between range2_from and range2_to
or date '2010-12-05' between range3_from and range3_to;
ref.SQL query for finding a value in multiple ranges

Store your times as DATETIME and use BETWEEN
So:
where myDate BETWEEN '2011-03-18 08:30' AND '2011-09-18 09:00'

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)

Better database basis for table with multiple date informations

What do you think is the better basis, in sense of "easyer to use" with SQL Syntax - the first or the second table?
Please give reasons.
table one:
+----+--------------------------------------+
| id | date1 | date2 | date3 |
+----+------------+------------+------------+
| 1 | 2014-02-15 | 2014-03-24 | 2014-03-24 |
| 2 | NULL | NULL | 2014-08-15 |
| 3 | 2014-06-13 | NULL | NULL |
| 4 | 2014-01-10 | 2014-09-14 | 2014-01-12 |
+----+------------+------------+------------+
table two:
+----+------------+-------+-------+-------+
| id | date | one | two | three |
+----+------------+-------+-------+-------+
| 1 | 2015-07-04 | true | true | false |
| 2 | 2014-06-13 | false | true | false |
| 3 | 2014-11-11 | true | false | false |
| 4 | 2017-03-02 | false | true | true |
+----+------------+-------+-------+-------+
(content of tables doesn't match in this example)
I just want to know if it is easier to deal with when you have just one date field and additional boolean fields instead of multiple date fields. For example if you want to have SELECTs like this
That depends what the dates are.
Just because two fields are both dates tell us nothing about what they have to do with each other, if anything.
If the three dates are totally unrelated and would never be interchangeable in processing, and if they are a fixed set that is not likely to change frequently, like "birth date", "hire date", and "next annual review date", then I would just make them three separate fields. Then when you write queries it would be very straightforward, like
select employee_id, name from employee where next_annual_review_date='2015-02-01'
On the other hand, if you might quite reasonably write a query that would search all three dates, then it makes sense to break the dates out into another table, with a field that identifies the specific date. Like I created a table once for a warehouse system where there were many dates associated with a stock item -- the date it arrived in the warehouse, the date it was sold, inventoried, returned to the warehouse (because the customer returned it, for example), re-sold, lost, damaged, repaired, etc. These dates could come in many possible orders, and many of them could occur multiple times. Like an item might be damaged, repaired, and then damaged and repaired again, or it could be sold, returned, sold again, and returned again, etc. So I created a table for the stock item with the "static" info like part number, description, and the bazillion codes that the user needed to describe the item, and then a separate "stock event" table with the stock item id, event code, the date, and various other stuff. Then there was another stock event table that listed the event codes with descriptions.
This made it easy to construct queries like, "List everything that has happened to this item in the past four years in date order", or "list all items added to the inventory in November", etc.
Your second table seems like an all-around bad idea. I can't think of any advantage to having 3 Boolean fields rather than one field that says what it is. Suppose the three dates are birth date, hire date, and next review date. You could create codes for these -- maybe 1,2, 3; maybe B, H, R; whatever. Then selecting on a specific event is easy enough either way, I guess: select date where hire = true versus select date where event = 'H'.
But listing multiple dates with a description is much easier with a code. You just need a table of codes and descriptions, and then you write
select employee_name, event_code, date
from employee e
join employee_event ev on ev.employee_id=e.employee_id
join event v on v.event_id=ev.event_id
where ... whatever ...
But with the Booleans, you'd need a three-way case/when.
What happens when new event types are added? With an event code, it's just a data change: add a enw record to the event code table. With the Booleans, you need to change the database.
You create the potential for ambiguous data. What happens if two of the Booleans are true, or if none of them are true? What does that mean? There's a whole category of error that can't possibly happen with event codes.
Neither of those are normalized. Normalization is a good way to avoid data anomalies and keep things DRY.
What do your dates represent? What does "one", "two", and "three" represent?
I would go with something like this:
create table my_table (
my_table_id int primary key,
a_more_descriptive_word_than_date date not null,
label text not null
);
The data would look like this:
id date label
1 2014-12-23 one
2 2014-12-24 two
3 2014-12-25 three

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.

What data type to be used for storing dates like '01-05'?

I want to store the list of all public holidays in a year.
Then the employees can avail the leaves for these days. The leave details will be stored in another table.
My issue is that I need to have a table for listing all the public holidays and the corresponding leaves.
Ex- 1st May must be listed for May Day.
However, I can't give date here (01-05-2014) because the same dates (01-05) will occur in each year.
So, how can I store these dates in a mysql table.
My current table structure is:
mysql> desc table;
+-----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+----------------+
| id | int(10) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | NO | | NULL | |
| leaveCode | text | NO | | NULL | |
| date | date | YES | | NULL | |
+-----------+--------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)
Here, I use date as the data type for the date.
But, It's not working.
When I tried inserting the values, the dates field is not getting populated.
<pre>
INSERT INTO table VALUES ('', 'May Day', 'MD', '01-05');
</pre>
I need a method to store these dates in the format - DD-MM in the table (Ex: 01-05).
Can someone please help me.
I'd rather prefer to store the day and the month in separate numeric fields, just because if you store a date, you must have to choose an arbitrary year with 366 days...
The concept of Date is a moment in time. You're not storing a date, although you could use the Date representation to store your infomation.
BUT, if you really want to use the Date column, you can choose some arbitrary leap year and set your "date" as YYYY-MM-DD
You can't store a holiday list as just month and day, because holidays vary from year-to-year on the calendars most commonly used for business. Religious holidays are notorious for wandering around the calendar. Consider:
Easter
Passover
Ramadan
Add to that holidays such as the Chinese New Year, American Thanksgiving, and American President's Day and you might start to think that calendar days have nothing to do with holidays. Okay, there are a few -- New Year's Day, May Day, Fourth of July, and Bastille Day. In the United States, there is a tendency to move national holidays onto a Monday, when the holiday would occur on the weekend.
You need a holiday table or calendar table with holiday information. When I've needed such a beast in the past, I've just used the holidays list provided with gmacs. I'm pretty sure there are other sources on the web for this information.

splitting a time period into calendar months -- mysql

I am basically facing the same problem as described in this thread: http://www.experts-exchange.com/Database/Oracle/10.x/Q_24693813.html. The difference is that I need to find a solution with MYSQL.
I am dealing with an Electricity Usage table:
ID Account FromDate ToDate ElecUse ElecDemand ElecBillAmt
2903 100009 2010-10-14 2010-11-12 352400 668.1 12592.53
2904 100009 2010-11-12 2010-12-15 426400 666 14284.39
2905 100009 2010-12-15 2010-01-14 406800 708.4 13812.54
2906 100009 2010-01-14 2010-02-15 443200 697.9 14514.99
I would like to report the usage on a monthly basis beginning with the start of each month and ending with the end of each month. Thus, I would like to have a MYSQL query that can produce the following table from the above:
Account FromDate ToDate ElecUse ElecDemand ElecBillAmt
100009 2010-10-14 2010-10-31 206579 391.51 7381.74
100009 2010-11-01 2010-11-30 378402 639.24 13002.12
100009 2010-12-01 2010-12-31 410778 680.46 13859.46
100009 2011-01-01 2011-01-31 425290 701.14 14156.77
100009 2011-02-01 2011-02-15 207750 327 6803.85
I appreciate the help,
Dan
Turning columns into rows is often the job of a self-join or a union.
In all of your examples, your FromDate is the month before the ToDate. How about one query to get the number of days in the first month, and another to get the leftover days in the second month? Allocating ElecUse between the two would be easy after that.
Based on your actual data, you might need more queries (the two dates are in the same month, etc).
select fromdate, last_day(fromdate) as todate,
datediff(last_day(fromdate), fromdate) as days_in_month,
datediff(todate, fromdate) as days_in_range,
datediff(last_day(fromdate), fromdate)/datediff(todate, fromdate) as pct_in_month,
elecuse * (datediff(last_day(fromdate), fromdate)/datediff(todate, fromdate)) as elecuse_in_month
...
I left in the intermediate columns so you could see what was going on:
+------------+------------+---------------+---------------+--------------+------------------+
| fromdate | todate | days_in_month | days_in_range | pct_in_month | elecuse_in_month |
+------------+------------+---------------+---------------+--------------+------------------+
| 2010-10-14 | 2010-10-31 | 17 | 29 | 0.5862 | 206579.3102 |
+------------+------------+---------------+---------------+--------------+------------------+
UNION that with another query that does the second month in the original row's range, and you should be all set.
Heed #OllieJones' warnings about ElecDemand, though.