Hello all,
I need to create a query for house search, that would match in database user entered data: date when they want to move in and leave, number of people they have in group and price per night.
Lets say user searched for house:
dates: from 2011-01-15 to 2011-03-01 (see on picture period A1C1), for 3 people, and he is willing to spend from $90 to $125 dollars per night.
This is my manual calculations for this search:
dates available in database
total number of dates user wants to stay is: 44 days
price for the first period 2011-01-15 to 2011-01-25 is 10 days * $100 = $1000
price for the second period 2011-01-25 to 2011-02-14 is 20 days * $120 = $2400
price for the third period 2011-02-14 to 2011-03-01 is 14 days * $140 = $1960
total average price per night = 1000 + 2400 + 1960 / 44 = $121.8
price and number of people matches user input, so we display this house
If you merge dates and calculate average price per night for the given period, search script should match array of data provided above.
My question is this: How my query should look like to calculate quickly if user data matches records in database.
I was thinking about using SQL DATEDIFF function and then multiply by price ... etc but it looks to me pretty complex.
I will appreciate any advice.
Thank you
UPDATE
Here is my database schema:
Table "apt_search_periods" which stores all merged dates (continuous dates from availability table)
+-----------+------------+------------+-----------+--------------+--------+
| period_id | start_date | end_date | rental_id | nb_of_people | merged |
+-----------+------------+------------+-----------+--------------+--------+
| 21 | 2011-03-31 | 2012-03-31 | 548 | 4 | y |
+-----------+------------+------------+-----------+--------------+--------+
Table "apt_search_periods_avail" linking merged dates with availability table
+----+-----------+-----------------+
| id | period_id | availability_id |
+----+-----------+-----------------+
| 21 | 21 | 20953 |
| 22 | 21 | 20952 |
| 23 | 21 | 4033 |
+----+-----------+-----------------+
Table "availability" with expanded dates and prices
+-------+-----------+------------+------------+--------------+--------------+
| id | rental_id | start_date | end_date | nb_of_people | rent_per_day |
+-------+-----------+------------+------------+--------------+--------------+
| 20952 | 548 | 2011-03-31 | 2011-07-01 | 4 | 575 |
| 4033 | 548 | 2011-07-01 | 2011-09-01 | 4 | 680 |
| 20953 | 548 | 2011-09-01 | 2012-03-31 | 4 | 575 |
+-------+-----------+------------+------------+--------------+--------------+
Following should get you started.
Note that the only difference is that the third period comprises 15 days io 14 according to DATEDIFF.
SQL Statement
;WITH q AS (
/* Kick of with the record where startdate < input < enddate */
SELECT date_start
, date_end
FROM #HouseSearch
WHERE date_start <= #date_start
AND date_end >= #date_start
AND nb_people >= #nb_people -- Only when number of people is adequate
UNION ALL
SELECT q.date_start
, hs.date_end
FROM q
INNER JOIN #HouseSearch hs ON hs.date_start = q.date_end
WHERE nb_people >= #nb_people -- Only when number of people is adequate
)
SELECT *
FROM (
-- Only return result if sequence exists between date range
SELECT date_start = MIN(date_start)
, date_end = MAX(date_end)
FROM q
WHERE date_end >= #date_end
) datetimerange
-- Calculate the average price
CROSS APPLY (
SELECT [AveragePrice] = SUM(price / DATEDIFF(dd, #date_start, #date_end))
FROM (
-- Price for all records where date_end <= #date_end
SELECT [price] =
CASE WHEN #date_start < date_start
THEN DATEDIFF(dd, date_start, date_end) * price
ELSE DATEDIFF(dd, #date_start, date_end) * price
END
FROM #HouseSearch
WHERE #date_end > date_end
UNION ALL
-- Price of remaining records where date_end >= #date_end
SELECT DATEDIFF(dd, date_start, #date_end) * price
FROM #HouseSearch
WHERE #date_end between date_start AND date_end
) prices
) price
WHERE date_start IS NOT NULL
Test data
DECLARE #HouseSearch TABLE (
date_start DATE
, date_end DATE
, nb_people INTEGER
, price FLOAT
)
INSERT INTO #HouseSearch VALUES
('2011-01-01', '2011-01-25', 4, 100)
, ('2011-01-25', '2011-02-14', 3, 120)
, ('2011-02-14', '2011-03-12', 3, 140)
, ('2011-03-12', '2011-04-10', 3, 100)
DECLARE #date_start DATE = '2011-01-15'
DECLARE #date_end DATE = '2011-03-01'
DECLARE #nb_people INTEGER = 3
DECLARE #price_low FLOAT = 90
DECLARE #price_high FLOAT = 15
Related
I have a travel history of the employee. I want to check, for the particular month, whether he is in outstation (traveled outside) or in the office, to calculate number of hours in travel. Just we are maintaining the travel database, in that we entered employee name with client place traveled with travel date and returned date.
One employee have the following data:
Traveled date: '2015-08-29' (29th Aug 2015)
returned date: '2015-11-06' (6th Nov 2015)
So here, I want to check in the month of October, all employees that are out of the office. Obviously this guy should come in that category, but I could not get him.
I also tried directly in MySQL workbench, but I didn't get the result.
My original PHP code:
// $req['date_start'] = '2015-10-01'
// $req['date_end'] = '2015-10-31'
$employeeTravel = new EmployeeTravelRecord();
$TravelEntryList = $employeeTravel->Find("(travel_date between ? and ? or return_date between ? and ? )",array($req['date_start'], $req['date_end'],$req['date_start'], $req['date_end']));
$startdate = $req['date_start'];
$enddate = $req['date_end'];
foreach($TravelEntryList as $Travelentry){
$key = $Travelentry->employee;
if($startdate >= $Travelentry->travel_date)
{
$firstdate = $startdate;
}
else
$firstdate = $Travelentry->travel_date;
if($enddate <= $Travelentry->return_date )
{
$lastdate = $enddate;
}
else
$lastdate = $Travelentry->return_date;
$holidays = $this->getholidays($firstdate,$lastdate);
$totalhours = $this->getWorkingDays($firstdate,$lastdate,$holidays); //It returns in total time of outstation in hours excluding company holidays
$amount = $totalhours;
if(isset($TravelTimeArray[$key])){
$TravelTimeArray[$key] += $amount;
}else{
$TravelTimeArray[$key] = $amount;
}
}
But my input data doesn't retrieve that particular employee record, because both traveled date and returned date don't fall in my input dates.
MySQL Workbench:
SELECT * FROM employeetravelrecords where travel_date between '2015-10-01' and '2015-10-31' or return_date between '2015-10-01' and '2015-10-31';
I got only the following result:
+----+----------+---------------+----------------+----------+------------------+------------------+----------------+
| id | employee | type | project | place | travel date | return date | details |
+----+----------+---------------+----------------+----------+------------------+------------------+----------------+
| 13 | 38 | International | PVMTC Training | Hongkong | 11/10/2015 13:33 | 28/11/2015 13:33 | PVMTC Training |
| 14 | 48 | International | PVMT | VIETNAM | 10/10/2015 9:28 | 1/1/2016 9:28 | PETRO |
| 17 | 57 | International | PVMT | Saudi | 10/10/2015 11:39 | 2/1/2016 11:39 | |
+----+----------+---------------+----------------+----------+------------------+------------------+----------------+
The following record didn't get retrieved by this query:
+---+----+---------------+------+-----+-----------------+-----------------+-----------------+
| 7 | 22 | International | MOHO | XYZ | 29/8/2015 18:00 | 6/11/2015 18:00 | FOR DDS review |
+---+----+---------------+------+-----+-----------------+-----------------+-----------------+
SELECT * FROM employeetravelrecords
WHERE
return_date >= '2015-10-01' /* start parameter */
and travel_date <= '2015-10-31' /* end parameter */
The logic seems a little mind-bending at first and I've even reordered the comparisons a little from my original comment above. Think of it this way: you need to return after the start of the range and leave before the end of the range in order to have an overlap.
For a longer explanation and discussion you might find this page useful: TestIfDateRangesOverlap
SELECT '2015-08-29' < '2015-10-31' AND '2015-11-06' >= '2015-10-01' on_leave;
+----------+
| on_leave |
+----------+
| 1 |
+----------+
You can use between for get data between two dates. Here is the working example.
Here is my table structure :
Table User
user_id user_name created_date modified_date
1 lalji nakum 2016-01-28 17:07:06 2016-03-31 00:00:00
2 admin 2016-01-28 17:25:38 2016-02-29 00:00:00
And here is my mysql query :
Query
SELECT * FROM `user` WHERE created_date between '2015-12-01' and '2016-12-31' or modified_date between '2015-12-01' and '2016-12-31'
Result of above query :
Result
user_id user_name created_date modified_date
1 lalji nakum 2016-01-28 17:07:06 2016-03-31 00:00:00
2 admin 2016-01-28 17:25:38 2016-02-29 00:00:00
You want to find all those people who are not available for the complete duration say between T1 & T2 (and T2 > T1). The query you used will only give users whose start date and return date both lie between the given interval which is not the required output.
So to get the desired output you need to check for all employees who have started their journey on or before T1 and return date is on or after T2 (thus unavailable for complete interval [T1, T2]). So the query you can use is:
SELECT * FROM employeetravelrecords where travel_date <= '2015-10-01' and return_date >= '2015-10-31';
If you want employees who are even partially not available between the given duration then we need employees who started their travel before T1 and have any return date later than T1 (thus making them atleast unavailable for a part of the given interval):
SELECT * FROM employeetravelrecords where travel_date <= '2015-10-01' and return_date > '2015-10-01';
Here is my database structure
Table name :set_inventory, and columns are below
inventory_id | room_id | quantity_start_date | quantity_end_date | total_rooms
1 | 2 | 2015-10-10 | 2015-10-12 | 5
2 | 2 | 2015-10-13 | 2015-10-14 | 10
3 | 2 | 2015-10-15 | 2015-10-17 | 0
Another Table
Table name : rooms, amd columns are
room_id | room_type | room_picture | room_description
2 | standard | pic_link | demo description
Description: In inventory table admin able to set the inventory based on multiple date ranges.
My query:
SELECT rooms.room_id, rooms.room_pic1, rooms.room_type, rooms.maximum_adults,
rooms.maximum_children, rooms.room_amenities,set_inventory.room_id,
set_inventory.quantity_start_date, set_inventory.quantity_start_date,
set_inventory.total_rooms
from rooms, set_inventory
WHERE rooms.room_id = set_inventory.room_id
AND quantity_start_date <= '2015-10-11'
AND quantity_end_date > '2015-10-13'
It shows me the inventory only 5, based in above query,
What i am looking for :actually, i am looking for the result,
for example my check_in_date "2015-10-11" and check_out_date is "2015-10-17"
In this i will be able to notify the customer that
"room is only available for 4 days,
Plz help me , thanks
UPDATED
Try this WHERE condition:
WHERE rooms.room_id = set_inventory.room_id AND DATE(quantity_start_date) <= '2015-10-11' AND DATE(quantity_end_date) > '2015-10-13';
So, minding you want to have the amount of days between these to dates, your query should contain somwthing like this:
SELECT ABS(DATEDIFF(a1.start_date, a2.end_date)) FROM (
SELECT quantity_start_date as start_date FROM set_inventory WHERE quantity_start_date <= '2015-10-11' ORDER BY start_date DESC LIMIT 1) a1, (
SELECT quantity_end_date as end_date FROM set_inventory WHERE quantity_end_date > '2015-10-13' ORDER BY end_date ASC LIMIT 1) a2;
If you try it, you'll get 4 in result.
I've a table in which write every subscription sold of my magazine, like this:
USERID | DATE |
31 | 2011-09-22 |
54 | 2011-09-22 |
59 | 2011-09-23 |
11 | 2011-10-02 |
88 | 2011-10-05 |
31 | 2011-10-06 |
17 | 2011-10-12 |
54 | 2011-10-15 |
31 | 2011-11-05 |
54 | 2011-11-12 |
Now, for statistical purpose, i need to having an outcome in which i see, for every single month, how many subscriptions i've sold and how many users have already bought once this.
For instance, if we look the datas on top, i should have an outcome likie this:
DATE | SOLD | RENEWAL
09 | 3 | 0
10 | 5 | 1
11 | 2 | 2
I can to group the subscriptions sold monthly, but i can't add the "renewal" info.
SELECT COUNT( * ) AS sold, MONTH(date) FROM table_sold WHERE date >= CURDATE() - INTERVAL 13 MONTH GROUP BY YEAR( date ) , MONTH( date ) ORDER BY date ASC LIMIT 1,15
In this way i only have an outcome like this:
DATE | SOLD
09 | 3
10 | 5
11 | 2
I've tried several option, with subquery, union and so on, but without successful.
There is to consider that the table has 70.000 entries and a query with a hard scan could be a problem.
In your opinion is there a way in mysql or i've to take the idea to make it in php?
Here is an idea. Look at the first date for each user. Then count renewals in every month that is not the first date:
select year(date), month(date), count(*) as sold,
sum(case when date <> firstdate then 1 else 0 end) as renewals
from subscriptions s join
(select userid, min(date) as firstdate
from subscriptions s
group by userid
) su
on s.userid = su.userid
group by year(date), month(date)
order by year(date), month(date);
Sorry if my question makes no sense. Not sure if we can do this with mysql only. Lets say I have this query:
SELECT SUM(win) * 100 as win_profit, date, uid FROM `tips` WHERE uid = 60 AND placed = 1 GROUP by date
This would obviously get the sum of the win column each day that is in the database.
Lets say the database had:
|___win___|____date____|
| 10 | 2014-04-16 |
| 10 | 2014-04-16 |
| 10 | 2014-04-17 |
| 10 | 2014-04-18 |
| 10 | 2014-04-18 |
| 10 | 2014-04-18 |
| 10 | 2014-04-19 |
| 10 | 2014-04-19 |
| 10 | 2014-04-19 |
This would result:
20
10
30
30
How can I get it to result so each adds up, mysql query only. So the result would be:
20
30
60
90
You could get all distinct dates, and LEFT JOIN to find the sum of all values up to that date; I kept the 100 multiplier from your sample query, but you need to remove it to get a result matching your desired result.
SELECT 100 * SUM(b.win), a.date
FROM (SELECT DISTINCT date FROM `tips`) a
LEFT JOIN tips b ON a.date >= b.date
GROUP BY a.date
ORDER BY a.date
An SQLfiddle to test with.
This could be another way to do it...
SET #full_sum=0;
SELECT #full_sum+SUM(win) as win_profit, date as this_date, uid,
#full_sum:=(SELECT #full_sum+SUM(win)
FROM `testing` WHERE uid = 60
GROUP by date HAVING date=this_date)
FROM `testing` WHERE uid = 60 GROUP by date;
I am using the following query,
SELECT
b.sales_id,b.category_id,a.bus_title,b.sales_title,b.sale_starts,b.sale_ends
FROM tbl_sales b INNER JOIN tbl_business a
ON a.bus_id=b.bus_id
WHERE b.active=1
AND CURDATE( ) < DATE_ADD(b.sale_ends, INTERVAL 14 DAY )
AND b.category_id='16' OR b.category_id
IN (SELECT cat_id FROM tb_category WHERE parent_id=16)
ORDER BY (b.sale_ends=CURDATE()) DESC,(b.sale_ends>CURDATE()) DESC,b.sale_ends ASC
and getting the result as shows below,
sales_id | category_id |bus_title | sales_title|sale_starts|sale_ends
----------|-------------|------------|------------|-----------|----------
36 | 17 | my bus | my sale |2012-04-03 |2012-05-03
35 | 19 | my bus 1 | my sale 1 |2012-04-03 |2012-05-03
42 | 16 | my bus 12 | my sale 12 |2012-04-05 |2012-05-05
10 | 17 | my bus 123| my sale 123|2011-12-15 |2011-12-18
I need to omit(not delete) sales after X amount of days past the End Date (here after 14 days). But the MySQL query i had written above returns the wrong results and displays the records with End Date 2011-12-18. How can i write the query in an effective way.
Need help. Thanks in advance.
Add this to your query :
AND sale_ends < DATE_SUB(NOW(), INTERVAL 14 day)