Querying available rental dates for a vehicle - php

I'm having trouble how to implement a search functionality on a website I'm working on.
The search form has two fields, starting date and an end date which a user wants to rent a vehicle.
In the database I have two tables, one for the vehicle and another one for available dates.
Table: cars
- car_id (int)
- name (varchar)
- description (varchar)
Table: cars_availability
- car_id (int)
- date (datetime)
- status (int) // 1: available 2: booked
Each row in cars_availability represent a day that the associated car is available for rent or booked.
Problem:
If user searches for a car that is available from 5.feb.2013 to 8.feb.2013 I would like to do a query to select all cars that are available for all the days in the users selection.
My current unfinished solution:
I know I can iterate through all the days using something like this
$begin = new \DateTime( date('Y-m-d', strtotime($data['rent_start'])));
$end = new \DateTime( date('Y-m-d', strtotime($data['rent_end'])));
$end = $end->modify( '+1 day' );
$interval = \DateInterval::createFromDateString('1 day');
$period = new \DatePeriod($begin, $interval, $end);
$available_cars = array();
foreach ( $period as $dayTime ){
// query the cars and joining the cars_availabilty table
$item = DB::select('cars.*')
->from('cars')
->join('cars_availability')->on('cars_availability.item_id', '=', 'cars.item_id')
->where('items_availability.date', '=', $dayTime->format("Y-m-d H:i:s"))
->and_where('items_availability.status', '=', 1)
->execute()->as_array();
if(count($item) > 0){
if(isset($available_cars[$item[0]['item_id']])){
$available_cars[$item[0]['item_id']][] = $item[0];
}else{
$available_cars[$item[0]['item_id']] = array();
$available_cars[$item[0]['item_id']][] = $item[0];
}
}
}
After this I would have an array of vehicles that might have all the days available but I would need to do some extra math to find that out.
I want know if there is some more elegant solutions to this problem, using a better query maybe.
If you can give me any feedback on this I would be very grateful.
Thanks

This is actually a fairly sane setup to query. Personally, I'd recommend following #inhan's advice, especially if it turns out cars can be booked for smaller slices of time (hours?). However, the queries get a little bit more complicated to write, because of edge-cases (if you look around here, you should find relevant examples).
Unfortunately, I can't speak to how this fits into your framework, but the SQL can look like this:
SELECT Cars.car_id, Cars.name, Cars.description
FROM Cars
JOIN (SELECT car_id
FROM Cars_Availability
WHERE `date` >= '2013-02-05'
AND `date` < '2013-02-09'
AND status = 1
GROUP BY car_id
HAVING COUNT(*) = DATEDIFF('2013-02-09', '2013-02-05')) Available
ON Available.car_id = Cars.car_id
(Have an SQL Fiddle Example)
This query assumes that there's only one entry per day, or this breaks. Basically, it grabs every 'available' row greater-than/equal the beginning date, and less than the 'end' date (date/time/timestamps are... complicated; this article deals with SQL Server-specific problems, but the basic concepts apply), and makes sure there's one for the count of days between them. Mostly, you were looking for the HAVING clause.
Given the proper index on Cars_Availability, it may not even hit the table, making this a good performer, too.

Related

Yii2, get an average value from timestamps

I have a basic table with orders, which have a field thats called created_at which is in timestamp format and I want to get the avarage on how many orders have been created per day.
Found a other similar qestion about something like mine question whichI have posted below in the hope that everybody understand what im trying to do.
//not working
$q = new \yii\db\Query();
$q->from('shopping_cart');
$total = $q->average('DATEDIFF(`created_at`, CURRENT_DATE())');
I believe it is more SQL related problem than Yii2. What you need (if I have understood it correctly) is:
count number of days from the beginning to today
count all the rows
divide these number to get the average.
I have tried this and it works fine
SELECT
count(*) / (select round((unix_timestamp() - (select min(created_at) from table)) / 86400))
from table;
back to Yii2: I believe you have to build this query manually
\Yii::$app->db->createCommand()
Method average in $q->average('DATEDIFF('created_at', CURRENT_DATE())'); just adds AVG(DATEDIFF('created_at', CURRENT_DATE())) to SQL command.
As Jiri Semmler said, what you want to do is about SQL not Yii.
All you need to do is find the count of records for the period you are interested in and divide it by the number of days of that period.
It can be something like
// Define period
$timeFrom = '2018-11-30';
$timeTo = '2018-12-02';
// Number of days for the period
$days = date_diff(date_create($timeFrom), date_create($timeTo))->format("%a");
// Query count of records between dates
$q = new \yii\db\Query();
$total = $q->from('order')
->where(['between', 'created_at', $timeFrom, $timeTo])
->count();
// Find average records per day
$average = $total / $days;
If you have Order model class:
// Query count of records between dates
$total = Order::find()
->where(['between', 'created_at', $timeFrom, $timeTo])
->count();

Select all rows from one table and one specific value for each row from another table

Good afternoon lads, I am trying to make a page where I can check which bosses I did today. I have two tables (table with bosses and table with boss times). Now I need to show all bosses but for each of them I only want to show the closest time when the boss is going to spawn.
The select so far looks like that:
$timePlus = strtotime($currentTime) + 60*60*2.2;
$timePlusForm = date("H:i:s", $timePlus);
$userNametoLower = strtolower($userName);
$userTableName = "".$userNametoLower."_bosses";
$currentTime = date("H:i:s", time());
"SELECT `bossTime`.`ID`, `bossTime`.`bossID`, `bossTime`.`time`, `$userTableName`.`ID`, `$userTableName`.`name`,
`$userTableName`.`zone`, `$userTableName`.`map`, `$userTableName`.`waypointCode`, `$userTableName`.`bossDone`
FROM `bossTime` LEFT JOIN `$userTableName` ON `$userTableName`.`ID` = `bossTime`.`bossID`
WHERE `bossTime`.`time` BETWEEN '$currentTime' AND '$timePlusForm'
GROUP BY `bossTime`.`bossID`
ORDER BY `bossTime`.`time` ASC";
The problem is that this select does not pick the next closest value from time table. I also tried BETWEEN and it also didn't work (some bosses got correct closest time but other got the second closest). Any idea how to solve this is welcomed.
I removed GROUP BY and changed the condition to WHERE bossTime.time >= '$currentTime' AND bossTime.time <='$timePlusForm' and for some reason it works

PHP Date and Time, getting the 1st, 2nd, 3rd, and 4th week in a month

I'm using ChartJS to build a few graphs for ticketing applications.
What I'm trying to do is query the MySQL database for # of tickets created for the 1st, 2nd, 4rd, and 4th week of the current month.
$month_num_tickets = array();
for ($x=0;$x<=30;$x=$x+6) {
$from = date("Y-m-d 00:00:00",strtotime('first day of this month +'.$x.' days', time()));
$to = date("Y-m-d 23:59:59",strtotime($from, time()));
$get = mysql_query("SELECT id FROM tickets WHERE date_created BETWEEN UNIX_TIMESTAMP('$from') AND UNIX_TIMESTAMP('$to') AND assigned_tech_uid='$uid'") or die(mysql_error());
$month_num_tickets[] = mysql_num_rows($get);
}
Not sure how to setup the loop...
You can get a nice tidy count in a single query like this:
SELECT
CEIL(DAYOFMONTH(FROM_UNIXTIME(date_created)) / 7) AS week_of_month,
COUNT(id) AS tickets_per_week
FROM tickets
WHERE YEAR(FROM_UNIXTIME(date_created)) = ?
AND MONTH(FROM_UNIXTIME(date_created)) = ?
GROUP BY `week_of_month`
ORDER BY `week_of_month` ASC
Note if you used native datetime or timestamp fileds, you could get rid of all of those FROM_UNIXTIME conversions. This is based on a "week" being the first 7 days of the month, not based on specific days of the week. If you wanted it based on fixed weeks (in terms of sunday through saturday or such) you could just use WEEK() function in the SELECT instead.
That might look like this:
SELECT
WEEK(FROM_UNIXTIME(date_created), 0) AS week_number,
COUNT(id) AS tickets_per_week
FROM tickets
WHERE YEAR(FROM_UNIXTIME(date_created)) = ?
AND MONTH(FROM_UNIXTIME(date_created)) = ?
GROUP BY `week_number`
ORDER BY `week_number` ASC
Here week_number would be a value between 0-53 and would not necessarily have any meaning for display other than as a means for aggregation. I am using mode 0 for WEEK() functoin as this specifies Sun-Sat week. You can look at the definitions here and determine what mode suits you the best: http://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_week
None of these queries are optimized because you won't be able to use an index on date_created . If you do get around to changing that column type to a datetime or timestamp, you would also perhaps want to change you WHERE condition to the slightly less easy to read, but more index friendly version like this:
WHERE date_created BETWEEN '2014-12-01 00:00:00' AND '2014-12-31 23:59:29'
I think this should work for you
SELECT YEARWEEK(date_created), MONTH(date_created), COUNT(*) FROM tickets
WHERE date_created BETWEEN UNIX_TIMESTAMP('$from') AND UNIX_TIMESTAMP('$to') AND
assigned_tech_uid='$uid'
GROUP BY YEARWEEK(date_created), MONTH(date_created)

PHP - MySQL query - counting results where timestamp is within specific date ("Y-m-d")

This is pretty basic MySQL, but I have not been able to figure this one out, how to do it correctly..
Example:
I have a DB table named "table1" with a list of records of user visitors data.
Columns:
"ID", "TM" and "IP"
"TM" contains timestamp for when the record is stored.
I have a PHP code where I loop through days from a start date to current day. Like this example:
// Start date
$startdateforarray = '2010-07-21';
// End date
$end_date = date("Y-m-d");
while (strtotime($startdateforarray) <= strtotime($end_date)) {
$timestamp = strtotime($startdateforarray);
//Here I want to run my MySQL Query...
$startdateforarray = date ("Y-m-d", strtotime("+1 day", strtotime($startdateforarray)));
}
Now, inside the loop I want to make a query to count how many results there are in "table1" for each day.
So the MySQL query should be something like:
"SELECT * FROM table1 WHERE TM = (day of $timestamp)"
Of course (day of $timestamp) is where I have a problem.
I know that this should be pretty simple to do, but I havent found a solution yet..
Assuming by timestamp you mean Unix Timestamp, you can do
SELECT * FROM table1 WHERE FROM_UNIXTIME(TM,'%Y-%m-%d') = '2010-07-21'

Find is a business is open: MySQL hours calculation

I have a list of business stored in a locations table, and stored in that table are hours the business opens and closes:
location
`mon_1_open`
`mon_1_closed`
`tue_1_open`
`tue_1_closed`
`wed_1_open`
`wed_1_closed`
ect...
I store the times in full hours and minutes, so say a business is open from 9:00AM to 5:30PM on monday.. mon_1_open = '900' AND mon_1_closed = '1730'.
I can't seem to figure out a way to find the day of week and output if the business is else open or closed based on the time of day.
Any suggestions?
This does not necessarily answer your question, but it may in the long run.
Your database scheme seems flawed. It definitely is not normalized. I would address that before it becomes a big issue, as you have noticed that it makes it hard to locate certain businesses hours. Here is a draft scheme that might be better suiting.
TABLE: locations
id INT AUTO_INCREMENT PRIMARY KEY
name VARCHAR(50)
TABLE: location_hours
id INT AUTO_INCREMENT PRIMARY KEY
location_id INT - Foreign Key references locations table
day CHAR(3) - (examples: mon, tue, wed, thu, fri, sat, sun)
hours VARCHAR(4) - (could also be int)
Then to get todays date, this can be done in MySQL with DATE_FORMAT %a, an example query:
SELECT locations.name, location_hours.hours
FROM locations
JOIN location_hours ON locations.id = location_hours.location_id
WHERE location_hours.day = DATE_FORMAT(NOW(), '%a')
AND location.name = 'Someway Business'
ORDER BY location_hours.hour
You should not need an open / close given that the the ORDER BY knows that 0900 < 1430 since it is a VARCHAR (although INT should know how to sort it as well), but your code when adding businesses will either need to update this record or you will need another field active to signify if that row should be used in the query. Just remember to use 24 hour time. Again this is a mock up, I just created it on the spot so it probably could use some improvements, but that would be better then doing a hack like you would have to with your current code.
UPDATE
Addressing the comment about finding if it is open or close:
Just use the PHP date function and call date('Hi') this will pull out the current time in 24-hour time, then you just do a simple if statement to see if it is between that, if it is, it is opened.
IE:
$sql = "SELECT locations.name, location_hours.hours
FROM locations
JOIN location_hours ON locations.id = location_hours.location_id
WHERE location_hours.day = DATE_FORMAT(NOW(), '%a')
AND location.name = 'Someway Business'
ORDER BY location_hours.hour";
$result = mysql_query($sql) or trigger_error("SQL Failed with Error: " . mysql_error());
$times = array();
while ($row = mysql_fetch_assoc($result)) {
if (empty($times['open'])) {
$times['open'] = $row['hours'];
}else {
$times['closed'] = $row['hours'];
}
}
$currentTime = date('Hi');
if ($times['open'] <= $currentTime
&& $times['closed'] > $currentTime) {
echo "Open";
}else {
echo "Closed";
}
Given that my logic is correct. Again, this is just pseudo code an example of usage. Given I just wrote it up on the spot. The above assumes you are only querying one business at a time.
$dayOfWeek = strtolower(date('D'));
$query = '
SELECT
location,
'.$dayOfWeek.'_1_open <= '.date('Gi').' AND
'.$dayOfWeek.'_1_closed >= '.date('Gi').' as is_open';
That should work.
However, you really should use a proper time datatype for the open/closed columns.

Categories