2

I am having a bit of trouble with an application I am working on and I hope to get some guidance.

I have a simple booking system: each item has a standard price, but there are some periods when the price can change.

For example:

  • Standard price is 100/day

  • From Oct. 1st to Feb. 1st of each year, the price is 80/day

  • From Jun. 1st to Aug. 31st of each year the price is 120/day

  • [...] up to 6 different price changes (seasons)

Because this is a yearly occurrence, I only need the month and day, but I dont't know how to store these values in the database. I was thinking of having two fields, one for month and one for day which hold numeric values(01-12 for month, 01-31 for day). These values are set by the user himself and they are relative to the item itself, it's not fixed for everyone.

The problem is that when someone makes a booking, I need to check if the price needs to be changed and I don't understand how to build the query to check this. To make maters worst, the price can have multiple values for a booked period.

Following the example above, If I were to book an item from Sep. 1st (current year) to Jul. 1st (next year), the price will be as follow:

  • 100/day from Sep. 1st to Sep. 30th

  • 80/day from Oct. 1st to Feb. 1st

  • 100/day from Feb. 2nd to May 31st

  • 120/day from Jun. 1st to Jul. 1st

Is there any way of checking these cases without having to loop each day and checking against the database for a match ?

Thank you

C. Ovidiu
  • 1,104
  • 2
  • 18
  • 38

2 Answers2

2

I don't know your database tables, but I think you could do like this:

  1. Get your period, iniDay, iniMonth, endDay, endMonth
  2. Your table need to have these fields: ini_day, ini_month, ini_daymonth, end_day, end_month, end_daymonth, price_day
  3. SELECT * from table WHERE iniDay >= booking_ini_day AND iniMonth >= booking_ini_month -- get initial period AND iniDay <= booking_end_day AND iniMonth <= booking_end_month UNION ALL SELECT * from table WHERE iniDay <= booking_ini_day AND iniMonth <= booking_ini_month -- get middle periods AND endDay >= booking_end_day AND endMonth >= booking_end_month UNION ALL SELECT * from table WHERE endDay >= booking_ini_day AND endMonth >= booking_ini_month AND endDay <= booking_end_day AND endMonth <= booking_end_month -- get end period
  4. Count days for each period/row in your result query and show to your user.

You will "duplicate" database information, like day, month and daymonth, but it's for better performance.

Felippe Duarte
  • 14,901
  • 2
  • 25
  • 29
  • Currently my table is structured like you described except I don't have a field for month and day combined. I will make some tests right away and get back to you. Thanks – C. Ovidiu May 18 '16 at 19:59
  • Unfortunately it does not work. The dates a user books do not necessarily fall between a range, but only some days may, so the where clause is not correct. – C. Ovidiu May 18 '16 at 20:12
  • Thank you, I'll try it. But seeing your query, what is the purpose of daymonth fields at this point if all the comparisons are made with single days and months ? – C. Ovidiu May 18 '16 at 20:40
  • 1
    If you need to do another comparison, query, order by, group by, add year or just show that information, it will be already in your hand. No need to cast or concat data. – Felippe Duarte May 18 '16 at 20:42
2

You can use MySQL date_format function when checking date.

For example:

select date_format('2016-05-18', '%m%d');
+-----------------------------------+
| date_format('2016-05-18', '%m%d') |
+-----------------------------------+
| 0518                              |
+-----------------------------------+
1 row in set (0,01 sec)

So, you can just check with simple interval: today is 0518 and its betwen Feb. 2nd and May 31st:

0202 <= 0518 <= 0531

Complete example:

desc mytable;
+-----------+---------+------+-----+---------+-------+
| Field     | Type    | Null | Key | Default | Extra |
+-----------+---------+------+-----+---------+-------+
| date_from | char(4) | YES  |     | NULL    |       |
| date_to   | char(4) | YES  |     | NULL    |       |
| price     | int(11) | YES  |     | NULL    |       |
+-----------+---------+------+-----+---------+-------+
3 rows in set (0,00 sec)

select * from mytable;
+-----------+---------+-------+
| date_from | date_to | price |
+-----------+---------+-------+
| 0901      | 0930    |   100 |
| 1001      | 0201    |    80 |
| 0202      | 0531    |   100 |
| 0601      | 0701    |   120 |
+-----------+---------+-------+
4 rows in set (0,00 sec)

Sample query:

select price, date_from, date_to from mytable where date_format(now(), '%m%d') between date_from and date_to;
+-------+-----------+---------+
| price | date_from | date_to |
+-------+-----------+---------+
|   100 | 0202      | 0531    |
+-------+-----------+---------+
1 row in set (0,01 sec)

EDIT:

Read your comment on @FelipeDuarte's answer... For period from 10/01 (Oct. 1st) to 02/01 (Feb. 1st), we need to consider the new year...

You can cheat by splitting the period in two ones: 10/01 to 12/31 and 01/01 to 02/01

Arcesilas
  • 1,388
  • 11
  • 24
  • Thank you very much for the help. So now I can find the price for a certain date. But my problem is that I cannot find the price(s) for a date range. Are you suggesting I query each day ? So if I book 10 days, from Sep.01 to Sep.11, I'll have to loop 10 times and find the right price. Is this right ? I know performance will be kind of bad, but I am looking at most 30 days for a booking. Thank you again – C. Ovidiu May 18 '16 at 20:25
  • 1
    Hopefully this [answer](http://stackoverflow.com/questions/143552/comparing-date-ranges) might be usefull. – Arcesilas May 18 '16 at 20:30
  • But I understand your question is slightly different: if the given period overlaps two periods, there will be 2 different prices... – Arcesilas May 18 '16 at 20:34
  • Yes, given a range the user wants to book an item, the price may be different on a day to day basis(worst case). Maybe two days it's 100, another two days is 80, three days is 150 and so on, depending on the total periods in the table. In the end I think I may just query each day and add the price: as I said, at most there will be maybe 30/40 days – C. Ovidiu May 18 '16 at 20:37
  • Or maybe you can do it in a different way. Query all periods (with their daily price) and check the appropriate ones depending on the booking period. It should be much faster and easier to manipulate in PHP. – Arcesilas May 18 '16 at 20:50