views:

249

answers:

3

Hi, I need to findout a better and quick way to query MySQL table to get specific data for each day in a given date range, where the table has two date columns specifying start and end date.

Table example: Promotions columns:

ID   startDate     EndDate     Discount     CategoryID  
=============================================================
1    2010/08/01    2010/08/10     10.00        A
2    2010/08/01    2010/08/10     15.00        B
3    2010/08/11    2010/08/15     05.00        A
4    2010/08/11    2010/08/15     10.00        B

I know I can grab the match promotion from the following query when two date ranges are given

SELECT * FROM Promotions WHERE (startDate <= "2010/08/12" AND endDate >= "2010/08/15") AND CategoryID = A

The result for the above query will fetch the row with ID 3

However if the two date ranges in the above query was changed to something like the following

SELECT * FROM Promotions WHERE (startDate <= "2010/08/12" AND endDate >= "2010/08/18") AND CategoryID = A

I am not getting any matched result, as I can understand the logic do not match any of the stored data in the table.

That is why, I need a better solution where I can get the result matched even if the end date exceeds more than the end date of a promotion. However changing the logic will not give me the best answer, suppose I can get the result if I use the following query but this also doesn't solve me whole problem if I need to find discounts valid of each and every day in the date range.

SELECT * FROM Promotions WHERE (startDate <= "2010/08/12" AND CategoryID = A

The true result I need is a way to convert this query into a loop or using a temporary MySQL table in the memmory to fetch results for each day as given below.

Result I need to find for the Date Range 2010/08/12 to 2010/08/18

===================================================================
Date           Discount    
=========================
2010/08/12      05.00
2010/08/13      05.00
2010/08/14      05.00
2010/08/15      05.00
2010/08/16      null
2010/08/18      null

I hope my question and problem is well explained and if you need any further clarifications regarding this problem, please let me know.

Hope to see a great answer for this.

+1  A: 

To query for items that are IN the date range, use:

SELECT * 
FROM Promotions 
WHERE (startDate >= "2010/08/12" AND endDate <= "2010/08/15") 
AND CategoryID = A

you want the start date the be greater than or equal, and the end date to be less than or equal.

Sander Rijken
Please note that I need to fetch the result by date as expressed in the question. That is the most important part of the question.
Raf
I think it's better to do that step in a language that's more suited for the job? Are you trying to show the table that's at the end of the question, or are you trying to determine whether there's a discount on a certain day?
Sander Rijken
@Sander, yes I am trying to find the discount on a period of days from a given date range but do not like to use php to query the database for each and every day, which is not very speedy.
Raf
Then do the query with startdate at the beginning of the range, enddate at the end of the range, that get's you all discounts for that range. Then loop through the range in php, and look in your results for discounts. That way you only have a single query
Sander Rijken
@Sander, that is an idea I did had in mind, but I was thinking of doing it with minimum coding efforts.
Raf
A: 

You need a calendar table of dates that looks something like:

Create Table Calendar ( [Date] DateTime Not Null Primary Key )

This table would be static and would need only be populated once. It would contain one row for each day covering any date range on which you would want to query. With that, your query is simpler:

Select C.Date, P.Discount
From Calendar As C
    Left Join Promotions As P
        On C.Date Between P.StartDate And P.EndDate
            And P.Category = 'A'
Thomas
I'm not sure wether this is a linq related question?
Sander Rijken
@Sander Rijken - I do not think the OP has anything to do with LINQ. In the fourth comment to the OP, Raf says that this is to be called from PHP.
Thomas
This approach if absolutely fine however I do not like to store a static calendar table in the database for my current project.
Raf
@Raf - If that is the case, then you are left with doing the processing in PHP. MySQL simply does not have the tools to dynamically build a calendar table on the fly and produce the result you want all in a single query. Why are you opposed to creating a calendar table? It is a single table with a single column that solves the problem?
Thomas
@Thomas I wonder what made me think you used LINQ, must've been after a long day :-)
Sander Rijken
@Raf - I should also mentioned that this will likely be faster than looping through the rows in PHP as you can get the results in a single query.
Thomas
@Raf- In looking at your solution, I have to say, it is convoluted but works in dynamically generating the equivalent of a calendar table. Still say that a calendar table is simpler, faster, easier to understand for other developers and 100% portable as is. But I'm glad you found a solution.
Thomas
@Thomas, I do agree with you to use a calendar table is more standardized design approach. I have to check your solution and compare mine and see the benchmark of the overall performance. According to the source where I found the approach of dynamic calendar, that says "If we extend the subquery to generate approx. 100,000 numbers (and thus about 274 years worth of dates), it runs in 0.0458 sec" which highly efficient. If I need a quick, faster solution.
Raf
@Raf - The real advantage to the dynamically generated table isn't going to be the performance so much as the savings in space. But space is cheap these days. I would guess the calendar table to be faster since the db can calculate an execution plan that should remain somewhat constant but you'd have to do some performance tests to know.
Thomas
Also, performance isn't the only thing that matters. It's quite important that the solution is easy to understand as well.
Sander Rijken
A: 

I guess I have found the solution for my problem by doing some research. I found a small query from the answer linked here which can generate dates for given range virtually.

So here is the single query which I have been able to get the result I need.

Select dr.Day, P.Discount

from
(
select a.Date as Day
from (
    select curdate() + INTERVAL (a.a + (10 * b.a) + (100 * c.a)) DAY as Date
    from (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 

union all select 7 union all select 8 union all select 9) as a
    cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all 

select 6 union all select 7 union all select 8 union all select 9) as b
    cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all 

select 6 union all select 7 union all select 8 union all select 9) as c
) a
where a.Date >= '2010-08-12' AND a.Date < '2010-08-18') dr LEFT JOIN

Promotions AS P ON dr.Day BETWEEN P.startDate AND P.EndDate
WHERE P.Category = 'A'

This have given me the result as what I wanted. Since I found the solution, I am presenting it here for others like me who might have similar question in the future.

I do thank you all that who have tried to help me solve this problem.

Raf
I'm sure it works, but it's not the right solution to the problem, you're making it overly complex this way. Consider the next guy that has to do maintenance here...
Sander Rijken
For rows of 10,000 in total, the Query took 0.0229 sec (from a given date range expanding from 2010/01/01 to 2100/01/01) and I say this is really speedy.
Raf
I do not know why this answer was down voted, since this answer solves the problem exactly as the way I wanted. The question also states that The true result I need is a way to convert a table with a query into a loop or using a temporary MySQL table in the memmory to fetch results for each day. So... not sure the reason why this is voted down.
Raf
I wasn't saying it cannot be fast, what I'm saying is that when someone else has to do maintenance on this code, he'll be scratching his head for quite some time before he figures out what you're actually doing there..
Sander Rijken