I'm working on a hotel booking app, where you can select a date and a room type and the application shows the available rooms. I'm using the Java Spring framework to do this.
This are the tables that i think matter to this query:
CREATE TABLE IF NOT EXISTS `booking` (
`id` bigint(20) NOT NULL,
`aproved` bit(1) NOT NULL,
`begin_date` datetime DEFAULT NULL,
`end_date` datetime DEFAULT NULL,
`room_id` bigint(20) DEFAULT NULL,
`user_id` bigint(20) DEFAULT NULL
) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS `room` (
`id` bigint(20) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`room_type_id` bigint(20) DEFAULT NULL
) ENGINE=InnoDB AUTO_INCREMENT=161 DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS `room_type` (
`id` bigint(20) NOT NULL,
`number_of_rooms` int(11) NOT NULL,
`price` int(11) NOT NULL,
`type` varchar(255) DEFAULT NULL,
`hotel_id` bigint(20) DEFAULT NULL
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=latin1;
I'm having dificulties making that query...
I made this one, but it's not a good idea to join the rooms with boookings because that will only select the rooms when there are bookings...
SELECT *
FROM room r join booking b on b.room_id = r.id join room_type rt on r.room_type_id = rt.id
WHERE not ((b.begin_date >= :initDate And b.begin_date <= :endDate) or (b.begin_date >= :initDate And b.end_date <= :endDate) or (b.begin_date <= :initDate and b.end_date >= :endDate) and b.aproved = true and rt.id = :roomType)
Any ideas?
select * from rooms r
where r.room_type_id = :desiredRoomType
and not exists (
select * from bookings b
where begin_date >= :desiredDate
and end_date <= :desiredDate
)
I am not sure, why begin_date/end_date might be null in your case, if they really can be, the query should reflect that.
Related
I have following database schema:
CREATE TABLE `property` (
`id` INT(11) PRIMARY KEY NOT NULL AUTO_INCREMENT,
`name` VARCHAR(100) NOT NULL
);
CREATE TABLE `venue` (
`id` INT(11) PRIMARY KEY NOT NULL AUTO_INCREMENT,
`property_id` INT(11) NOT NULL,
`name` VARCHAR(100) NOT NULL
);
CREATE TABLE `venue_available` (
`id` INT(11) PRIMARY KEY NOT NULL AUTO_INCREMENT,
`venue_id` INT(100) NOT NULL,
`day` VARCHAR(10) NOT NULL,
`from_time` TIME NOT NULL,
`to_time` TIME NOT NULL,
`lead_time_in_minutes` INT(11)
);
CREATE TABLE `venue_unavailable` (
`id` INT(11) PRIMARY KEY NOT NULL AUTO_INCREMENT,
`venue_id` INT(100) NOT NULL,
`from_datetime` DATETIME NOT NULL,
`to_datetime` DATETIME NOT NULL
);
CREATE TABLE `venue_reservation` (
`id` INT(11) PRIMARY KEY NOT NULL AUTO_INCREMENT,
`venue_id` INT(100) NOT NULL,
`start_datetime` DATETIME NOT NULL,
`end_datetime` DATETIME NOT NULL
);
I want to find properties having venues available from 25th Aug(Sat) to 27th August (Mon) from 10am to 3pm
Here is the SQL query I tried
SELECT
p.id,
p.name AS property_name,
v.name AS venue_name
FROM
venue v
LEFT JOIN
property p ON v.property_id = p.id
-- venue_available
LEFT JOIN
venue_available va_0 ON va_0.venue_id = v.id
LEFT JOIN
venue_available va_1 ON va_1.venue_id = v.id
WHERE 1 = 1
-- venue_available
AND (
(va_0.day = 'sat' AND va_0.from_time <= '2018-08-25 10:00:00' AND va_0.to_time >= '2018-08-25 15:00:00') AND
(va_1.day = 'sun' AND va_1.from_time <= '2018-08-26 10:00:00' AND va_1.to_time >= '2018-08-26 15:00:00')
)
-- venue_unavailable
AND NOT EXISTS (SELECT * FROM venue_unavailable vu WHERE '2018-08-25 10:00:00' <= vu.to_datetime AND '2018-08-26 15:00:00' >= vu.from_datetime)
GROUP BY
p.id;
The problem with the current query is, the condition for venue_available in SQL query seems to work correctly, but when I add the condition for venue_unavailable it returns me the empty result, however based on the data I am expecting 1 result.
Here is the link to SQL fiddle, if you want to play around with schema and fixtures
http://sqlfiddle.com/#!9/33d60f/10
Here is what I am trying to do
1. Get the list of all properties (not venues)
2. List the property only if one or more venue is available after checking with
venue_available
venue_unavailable
venue_reservation
Can you help me with how to go about this?x
Thank you.
UPDATE1
I followed the following post to determine overlapping dates in venue_unavailable Select rows that are not between dates (reservation)
Alright, so the way I solved it is using sub query which is working now.
I am now using the WHERE clause with something like this
WHERE v.id NOT IN (SELECT venue_id FROM provider_block pb WHERE :start_datetime <= pb.to_date AND :end_datetime >= pb.from_date)
This seems to do the job for now.
I have problem with sql query. The idea is to select all loans that are after payment (status 1/2/3) between 8 and 21 days with calculated value from payment_day til now.
I have already done some query but can't use columns days_after_payment and days_after_part_payment in WHERE section. I would like to have one column like days_after_payment based on loan type.
SELECT l.*,
(SELECT SUM(`value`) FROM `loan_part` WHERE `loan_id` = l.id AND `paid`=0) AS left_to_pay,
-(DATEDIFF((SELECT date FROM `loan_part` WHERE `loan_id` = l.id AND `paid`=0 AND `date`<CURDATE() ORDER BY `date` LIMIT 1), NOW())) AS days_after_part_payment,
-(DATEDIFF(l.payment_date, NOW())) AS days_after_payment
FROM loan l
WHERE (l.type=1 or l.type=2) AND (l.status=1 OR l.status=2 OR l.status=3)
GROUP BY l.client_id
ORDER BY
CASE l.type
WHEN 1 THEN days_after_payment
WHEN 2 THEN days_after_part_payment
ELSE 1 END
ASC
CREATE TABLE IF NOT EXISTS `loan` (
`id` int(11) NOT NULL,
`value` int(11) NOT NULL,
`client_id` int(11) NOT NULL,
`status` int(11) NOT NULL,
`type` int(11) NOT NULL,
`payment_date` date DEFAULT NULL
) ENGINE=MyISAM AUTO_INCREMENT=2068 DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `loan_part` (
`id` int(10) unsigned NOT NULL,
`loan_id` int(11) NOT NULL,
`value` float NOT NULL,
`date` date DEFAULT NULL,
`paid` tinyint(1) NOT NULL DEFAULT '0'
) ENGINE=MyISAM AUTO_INCREMENT=1751 DEFAULT CHARSET=utf8;
Update1 : I had to cut unnecessary columns and rewrite it into English from my native language.
ORDER BY 7
"7" means the 7th field in the SELECT. That works for GROUP BY also. I had to see the table definition to count how many in l.*.
How come id is not declared AUTO_INCREMENT?
I have 2 tables, that i want to join, one is rooms and another is reservations.
Basically I want to search for rooms which are not reserved (not in reservation table) and to get the details of those rooms (which are not in reservation table) from room table.
Here are my tables structure:
CREATE TABLE `room` (
`roomID` int(11) NOT NULL AUTO_INCREMENT,
`hotelID` int(11) NOT NULL,
`roomtypeID` int(11) NOT NULL,
`roomNumber` int(11) NOT NULL,
`roomName` varchar(255) NOT NULL,
`roomName_en` varchar(255) NOT NULL,
`roomDescription` text,
`roomDescription_en` text,
`roomSorder` int(11) NOT NULL,
`roomVisible` tinyint(4) NOT NULL,
PRIMARY KEY (`roomID`)
) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=utf8;
CREATE TABLE `reservation` (
`reservationID` int(11) NOT NULL AUTO_INCREMENT,
`customerID` int(11) NOT NULL,
`hotelID` int(11) NOT NULL,
`reservationCreatedOn` datetime NOT NULL,
`reservationCreatedFromIp` varchar(255) CHARACTER SET greek NOT NULL,
`reservationNumberOfAdults` tinyint(4) NOT NULL,
`reservationNumberOfChildrens` tinyint(4) NOT NULL,
`reservationArrivalDate` date NOT NULL,
`reservationDepartureDate` date NOT NULL,
`reservationCustomerComment` text CHARACTER SET greek,
PRIMARY KEY (`reservationID`)
) ENGINE=InnoDB AUTO_INCREMENT=47 DEFAULT CHARSET=utf8;
CREATE TABLE `reservationroom` (
`reservationroomID` int(11) NOT NULL AUTO_INCREMENT,
`reservationID` int(11) NOT NULL,
`hotelID` int(11) NOT NULL,
`roomID` int(11) NOT NULL,
PRIMARY KEY (`reservationroomID`)
) ENGINE=InnoDB AUTO_INCREMENT=47 DEFAULT CHARSET=utf8;
Here is the query that I have right now, which gives me wrong results:
SELECT * FROM room r
LEFT JOIN reservation re
ON r.hotelID = re.hotelID
WHERE re.hotelID = 13
AND NOT
(re.reservationArrivalDate >= '2014-07-07' AND re.reservationDepartureDate <= '2014-07-13')
I also have created a fiddle, with the data from both tables included:
http://sqlfiddle.com/#!2/4bb9ea/1
Any help will be deeply appreciated
Regards, John
i agree that room number was missed,
but query template should looks like
SELECT
*
FROM
room r
LEFT JOIN reservation re
ON r.hotelID = re.hotelID
WHERE r.hotelID = 2
AND NOT (
re.hotelID IS NOT NULL
AND re.reservationArrivalDate >= '2014-07-07'
AND re.reservationDepartureDate <= '2014-09-23'
) ;
You need change table in where statement from reservation to room. Also you need add re.hotelID to where statement as well, because on where statement you need check that record is not null ans only after try to check dates
Given the newly-added reservationroom table, consider using a NOT EXISTS sub-query to find rooms without reservations:
SELECT
*
FROM
room r
WHERE NOT EXISTS
(SELECT
*
FROM
reservationroom rr
WHERE
rr.reservationroomID = r.roomID
)
I have to following 3 tables: room, reservation and reservationroom
Their structure is as follows:
CREATE TABLE `room` (
`roomID` int(11) NOT NULL AUTO_INCREMENT,
`hotelID` int(11) NOT NULL,
`roomtypeID` int(11) NOT NULL,
`roomNumber` int(11) NOT NULL,
`roomName` varchar(255) NOT NULL,
`roomName_en` varchar(255) NOT NULL,
`roomDescription` text,
`roomDescription_en` text,
`roomSorder` int(11) NOT NULL,
`roomVisible` tinyint(4) NOT NULL,
PRIMARY KEY (`roomID`)
) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=utf8;
CREATE TABLE `reservation` (
`reservationID` int(11) NOT NULL AUTO_INCREMENT,
`customerID` int(11) NOT NULL,
`hotelID` int(11) NOT NULL,
`reservationCreatedOn` datetime NOT NULL,
`reservationCreatedFromIp` varchar(255) CHARACTER SET greek NOT NULL,
`reservationNumberOfAdults` tinyint(4) NOT NULL,
`reservationNumberOfChildrens` tinyint(4) NOT NULL,
`reservationArrivalDate` date NOT NULL,
`reservationDepartureDate` date NOT NULL,
`reservationCustomerComment` text CHARACTER SET greek,
PRIMARY KEY (`reservationID`)
) ENGINE=InnoDB AUTO_INCREMENT=46 DEFAULT CHARSET=utf8;
CREATE TABLE `reservationroom` (
`reservationroomID` int(11) NOT NULL AUTO_INCREMENT,
`reservationID` int(11) NOT NULL,
`hotelID` int(11) NOT NULL,
`roomID` int(11) NOT NULL,
PRIMARY KEY (`reservationroomID`)
) ENGINE=InnoDB AUTO_INCREMENT=46 DEFAULT CHARSET=utf8;
(please note that foreign keys have been removed from create statements for sake of simplicity)
What I am trying to do: I want to get all rooms that are not reserved for specific dates, that is only the free rooms from the specific hotel (I have its ID)
Here is the query that I have right now:
SELECT r.* FROM room r
LEFT JOIN `reservationroom` rr
ON r.`hotelID` = rr.`hotelID`
AND r.`roomID` = rr.`roomID`
LEFT JOIN `reservation` re
ON rr.`reservationID` = re.`reservationID`
WHERE (rr.`reservationroomID` = ''
OR rr.`reservationroomID` IS NULL
AND re.`reservationArrivalDate` >= 2014-08-27
AND re.`reservationDepartureDate` <= 2014-08-29
AND r.`hotelID` = 10
AND r.`roomVisible` = 1);
This query now returns 0 results. It should return 9 records, since the hotel with ID = 10 has 9 rooms that are free (no resevations for specific dates exist in the reservation table)
Can anyone give me a hand with this please? I am trying to sort this out couple of hours, without any success.
You are using left join, so conditions on all but the first table should be in the on clauses. I think you want a query more like this:
SELECT r.*
FROM room r LEFT JOIN
`reservationroom` rr
ON r.`hotelID` = rr.`hotelID` AND
r.`roomID` = rr.`roomID` LEFT JOIN
`reservation` re
ON rr.`reservationID` = re.`reservationID` AND
re.`reservationArrivalDate` >= 2014-08-27 AND
re.`reservationDepartureDate` <= 2014-08-29
WHERE r.`hotelID` = 10 AND r.`roomVisible` = 1 AND re.reservationID is null;
I'm not sure what the comparison is to the empty string. It doesn't seem necessary for this purpose.
I have this query which returns what I want.
SELECT DISTINCT c.cust_id
FROM tbCustomers AS c
JOIN tbOrders AS o
ON o.order_custid = c.cust_id
JOIN tbPayments AS p
ON p.pay_custid = c.cust_id
WHERE (
DATE(o.order_date) > DATE_ADD(NOW(), INTERVAL -14 DAY)
AND o.order_status = '3'
AND o.order_exported = '0'
)
OR (
DATE(p.pay_date) > DATE_ADD(NOW(), INTERVAL -14 DAY)
AND p.pay_exported = '0'
)
However it is (relatively) slow to execute and I'd like to find a way to speed it up.
Can anyone give me a few pointers?
EDIT:
EXPLAIN AND CREATE as requested
EXPLAIN:
(with db name removed)
CREATE(S):
CREATE TABLE IF NOT EXISTS `tbCustomers` (
`cust_id` int(11) NOT NULL,
`cust_roundid` int(11) DEFAULT NULL,
`cust_email` varchar(250) DEFAULT NULL,
`cust_pass` varchar(32) DEFAULT NULL,
`cust_firstname` varchar(50) NOT NULL,
`cust_surname` varchar(50) NOT NULL,
`cust_address1` varchar(50) NOT NULL,
`cust_address2` varchar(50) DEFAULT NULL,
`cust_town` varchar(50) DEFAULT NULL,
`cust_county` varchar(50) DEFAULT NULL,
`cust_postcode` varchar(10) NOT NULL,
`cust_phone` varchar(20) DEFAULT NULL,
`cust_evephone` varchar(20) DEFAULT NULL,
`cust_delpointid` int(11) DEFAULT NULL,
`cust_notes` text NOT NULL,
`cust_delnotes` text,
`cust_delorder` int(10) NOT NULL DEFAULT '0',
`cust_likes` text,
`cust_dislikes` text,
`cust_active` int(1) NOT NULL DEFAULT '1',
`cust_auth` int(1) NOT NULL,
`cust_newsletter` tinyint(4) NOT NULL DEFAULT '1',
`cust_rep` varchar(250) DEFAULT NULL,
`cust_join` datetime DEFAULT NULL,
`cust_howheard` varchar(25) DEFAULT NULL,
`cust_friendname` varchar(50) DEFAULT NULL,
`cust_friend1staddress` varchar(50) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=18694 ;
-- --------------------------------------------------------
--
-- Table structure for table `tbOrders`
--
CREATE TABLE IF NOT EXISTS `tbOrders` (
`order_id` int(11) NOT NULL,
`order_custid` int(11) NOT NULL,
`order_date` datetime NOT NULL,
`order_status` int(1) NOT NULL,
`order_exported` tinyint(1) NOT NULL DEFAULT '0',
`order_subid` int(11) DEFAULT NULL,
`discount_vouchers` longtext
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=310575 ;
-- --------------------------------------------------------
--
-- Table structure for table `tbPayments`
--
CREATE TABLE IF NOT EXISTS `tbPayments` (
`pay_id` int(11) NOT NULL,
`pay_custid` int(11) NOT NULL,
`pay_date` datetime NOT NULL,
`pay_value` float NOT NULL,
`pay_detail` varchar(200) NOT NULL,
`pay_stref` varchar(50) DEFAULT NULL,
`pay_nomcode` int(5) NOT NULL,
`pay_exported` tinyint(1) NOT NULL DEFAULT '0'
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=128264 ;
--
-- Indexes for dumped tables
--
--
-- Indexes for table `tbCustomers`
--
ALTER TABLE `tbCustomers`
ADD PRIMARY KEY (`cust_id`), ADD KEY `cust_name` (`cust_firstname`,`cust_surname`), ADD KEY `cust_email` (`cust_email`), ADD KEY `cust_newsletter` (`cust_newsletter`);
--
-- Indexes for table `tbOrders`
--
ALTER TABLE `tbOrders`
ADD PRIMARY KEY (`order_id`), ADD KEY `order_custid` (`order_custid`), ADD KEY `order_date` (`order_date`), ADD KEY `order_status` (`order_status`), ADD KEY `order_exported` (`order_exported`), ADD KEY `order_subid` (`order_subid`);
--
-- Indexes for table `tbPayments`
--
ALTER TABLE `tbPayments`
ADD PRIMARY KEY (`pay_id`);
--
-- AUTO_INCREMENT for dumped tables
--
--
-- AUTO_INCREMENT for table `tbCustomers`
--
ALTER TABLE `tbCustomers`
MODIFY `cust_id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=18694;
--
-- AUTO_INCREMENT for table `tbOrders`
--
ALTER TABLE `tbOrders`
MODIFY `order_id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=310575;
--
-- AUTO_INCREMENT for table `tbPayments`
--
ALTER TABLE `tbPayments`
MODIFY `pay_id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=128264;
Also, apologies for the incorrect column name in the where clause - fixed now
Cheers!
I have assumed that pay_custid should be replaced with pay_date in the WHERE clause.
Rewrite the query to:
SELECT DISTINCT c.cust_id
FROM tbCustomers AS c
JOIN tbOrders AS o
ON o.order_custid = c.cust_id
JOIN tbPayments AS p
ON p.pay_custid = c.cust_id
WHERE (
o.order_date > CURDATE() - INTERVAL 14 DAY
AND o.order_status = 3
AND o.order_exported = 0
)
OR (
p.pay_date > CURDATE() - INTERVAL 14 DAY)
AND p.pay_exported = 0
)
This allows the optimizer to use an index on order_date and pay_date, if it is wrapped in the DATE function the indexes cannot be used.
I'd add indexes to both date fields, but a clever composite index on each table may be more flexible.
Even better may be:
SELECT DISTINCT c.cust_id
FROM tbCustomers AS c
LEFT JOIN tbOrders AS o
ON o.order_custid = c.cust_id
AND o.order_date > CURDATE() - INTERVAL 14 DAY
AND o.order_status = 3
AND o.order_exported = 0
LEFT JOIN tbPayments AS p
ON p.pay_custid = c.cust_id
AND p.pay_date > CURDATE() - INTERVAL 14 DAY)
AND p.pay_exported = 0
WHERE (o.order_cust_id IS NOT NULL OR p.pay_cust_id IS NOT NULL)