How to verify if room is fully booked? - php
I am currently working on a booking system . I'm currently encountering a problem in finding out if an apartment is fully booked. In my database i have a table holding all the apartments and their details. I am trying to get the dates that all apartments for example with 4 bedrooms that are booked. I am running the following sql to return the booked dates of all 4 bedroom apartments.
SELECT *
FROM `apartment_booking` AS ab
JOIN apartment AS a ON ( a.id = apartmentId )
JOIN booking AS b ON ( b.id = bookingId )
WHERE bedrooms = '4'
ORDER BY checkIn
The return of the sql is
id CheckIn checkOut userId
74 2014-04-15 2014-04-22 1
75 2014-04-15 2014-04-22 1
102 2014-06-03 2014-07-07 1
71 2014-06-16 2014-06-23 1
114 2014-07-19 2014-08-02 1
121 2014-07-20 2014-08-02 1
57 2014-07-22 2014-08-05 1
122 2014-07-28 2014-08-02 1
117 2014-08-03 2014-08-10 1
As i have 4 apartments in the system with four bedrooms i would like to get the dates that all four bedrooms are booked.
Example with the output got the dates 2014-07-28 till 2014-08-02 are fully booked as in that date range there are in total four bookings.
Database:
CREATE TABLE `apartment` (
`id` int(11) NOT NULL auto_increment,
`code` varchar(4) NOT NULL,
`bedrooms` int(11) NOT NULL,
`description` varchar(500) default NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=26 ;
--
-- Dumping data for table `apartment`
--
INSERT INTO `apartment` (`id`, `code`, `bedrooms`, `description`) VALUES
(1, '1c', 3, ''),
(4, '4d', 4, NULL),
(5, '5b', 2, NULL),
(10, '10c', 3, NULL),
(11, '11b', 2, NULL),
(12, '12d', 4, NULL),
(13, '13c', 3, NULL),
(14, '14a', 1, 'Yo'),
(15, '15b', 2, NULL),
(16, '16b', 2, NULL),
(17, '17d', 4, NULL),
(22, '22d', 4, NULL),
CREATE TABLE `apartment_booking` (
`id` int(11) NOT NULL auto_increment,
`apartmentId` int(11) NOT NULL,
`bookingId` int(11) NOT NULL,
`ref` varchar(50) NOT NULL,
`pax` int(11) NOT NULL default '1',
`remarks` varchar(500) default NULL,
`guestFullName` varchar(30) default NULL,
`guestCountry` varchar(2) default NULL,
`guestFlightDetails` varchar(200) default NULL,
PRIMARY KEY (`id`),
KEY `apartmentId` (`apartmentId`),
KEY `bookingId` (`bookingId`),
KEY `ref` (`ref`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=179 ;
--
-- Dumping data for table `apartment_booking`
--
INSERT INTO `apartment_booking` (`id`, `apartmentId`, `bookingId`, `ref`, `pax`, `remarks`, `guestFullName`, `guestCountry`, `guestFlightDetails`) VALUES
(164, 1, 140, 'Hotelbeds', 5, '', 'Andrew Robertson', 'MT', '')
(165, 21, 141, 'Hotelbeds', 6, '', 'Pipitone', 'MT', ''),
(166, 5, 142, 'maltaholidaylets', 2, '', 'holly turpin', 'MT', ''),
(167, 12, 143, 'direct003', 4, '', 'Bernard Walch', 'MT', ''),
(168, 17, 144, 'meetingpoint', 4, '', 'Edvin Modigh', 'MT', ''),
(169, 23, 145, 'direct', 3, '', 'Andrea bacchetti', 'MT', ''),
(172, 25, 148, 'direct', 5, '', 'Wimold Peters', 'MT', ''),
(173, 20, 149, '7228110687', 4, '', 'Ms. Benedetta Tombari', 'MT', ''),
(174, 23, 149, '7228110687 meetingpoint', 2, '', 'Ms. Milena Moretti', 'MT', ''),
(175, 25, 150, 'meetingpoint', 6, '', 'N Burdett', 'MT', ''),
(176, 8, 151, 'Hotelbeds', 2, '', 'tito titti', 'MT', ''),
(177, 1, 152, 'meetingpoint', 3, '', 'Stephen Mckenna', 'MT', ''),
(178, 16, 153, 'mhcs', 4, '', 'Wojclech Blaszak', 'MT', '');
-- --------------------------------------------------------
--
-- Table structure for table `booking`
--
CREATE TABLE `booking` (
`id` int(11) NOT NULL auto_increment,
`reference` varchar(20) NOT NULL,
`dateTime` datetime NOT NULL,
`checkIn` date NOT NULL,
`checkOut` date NOT NULL,
`userId` int(11) default NULL,
PRIMARY KEY (`id`),
KEY `agent` (`userId`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=154 ;
--
-- Dumping data for table `booking`
--
INSERT INTO `booking` (`id`, `reference`, `dateTime`, `checkIn`, `checkOut`, `userId`) VALUES
(136, 'euroresort booking.b', '2014-07-02 09:30:08', '2014-08-04', '2014-08-11', 1),
(137, '7014505534', '2014-07-02 09:32:05', '2014-07-19', '2014-07-24', 1),
(138, 'BR4277518', '2014-07-02 09:45:02', '2014-08-09', '2014-08-16', 1),
(139, '100206154', '2014-07-02 10:11:45', '2014-07-27', '2014-08-03', 1),
(140, '120-135249-95', '2014-07-02 10:13:14', '2014-07-02', '2014-07-03', 1),
(141, '120-135181-94', '2014-07-02 10:14:31', '2014-08-10', '2014-08-17', 1),
(142, '000548MHL', '2014-07-02 12:38:54', '2014-08-25', '2014-09-01', 1),
(143, 'direct003', '2014-07-02 15:48:04', '2014-08-11', '2014-08-22', 1),
(144, 'SH3049361', '2014-07-02 15:52:18', '2014-08-05', '2014-08-14', 1),
(145, 'direct009', '2014-07-03 08:27:56', '2014-07-19', '2014-07-26', 1),
(148, 'direct010', '2014-07-04 08:12:13', '2014-07-08', '2014-07-22', 1),
(149, '7228110687', '2014-07-04 13:28:16', '2014-08-10', '2014-08-16', 1),
(150, '7308310623', '2014-07-07 08:39:04', '2014-08-11', '2014-08-20', 1),
(151, '120-135677-92', '2014-07-07 08:43:06', '2014-08-22', '2014-08-29', 1),
(152, '100209964', '2014-07-07 10:59:16', '2014-08-05', '2014-08-12', 1),
(153, 'mhcs', '2014-07-07 13:07:22', '2014-08-08', '2014-08-16', 1);
It gets a bit complicated.
The following query generates a range of numbers from 0 to 999, and adds each number as a number of days to the checkIn date for each booking, where the resulting date is less than or equal to the checkOut date for bookings for apartments with 4 rooms. This should give one row per apartment per day booked.
The number of booking ids for each date is then counted, and compared with the number of apartments with 4 bedrooms (from a sub query). The HAVING clause then discards all rows for dates where the number of aparments booked is not the same as the number of apartments with 4 rooms.
SELECT aBookedDate, sub2.apartment_cnt, COUNT(id) AS all_booking_cnt
FROM
(
SELECT booking.id, DATE_ADD(booking.checkIn, INTERVAL iCnt DAY) AS aBookedDate
FROM
(
SELECT units.i + tens.i * 10 + hundreds.i * 100 AS iCnt
FROM (SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9)units
CROSS JOIN (SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9)tens
CROSS JOIN (SELECT 0 i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9)hundreds
) sub0
CROSS JOIN booking
INNER JOIN apartment_booking ON booking.id = apartment_booking.bookingId
INNER JOIN apartment ON apartment.id = apartment_booking.apartmentId
WHERE DATE_ADD(booking.checkIn, INTERVAL iCnt DAY) <= booking.checkOut
AND apartment.bedrooms = 4
) sub1
CROSS JOIN
(
SELECT COUNT(*) AS apartment_cnt
FROM apartment
WHERE bedrooms = 4
) sub2
GROUP BY aBookedDate
HAVING all_booking_cnt = sub2.apartment_cnt
SQL fiddle for it:-
http://www.sqlfiddle.com/#!2/6edbe/5
You need left outer join, so you also show the apartments that are not booked.
SELECT *
FROM `apartment_booking` AS ab
JOIN apartment AS a ON ( a.id = apartmentId )
JOIN booking AS b ON ( b.id = bookingId )
WHERE bedrooms = '4' and userId is null
ORDER BY checkIn
Ones that have null user id will be empty (aka non-booked), since there is no booking connected to that apartment. You didn't say enough about the structure so I take it you delete the bookings rather than keeping history. If you keep all historical entries you need to check the date with today's date instead.
SELECT *
FROM `apartment_booking` AS ab
JOIN apartment AS a ON ( a.id = apartmentId )
LEFT OUTER JOIN booking AS b ON ( b.id = bookingId )
WHERE bedrooms = '4' and checkOut > NOW()
ORDER BY checkIn
EDIT:
It should look something like, I'll try to prepare a fiddle with that later:
SELECT
(COUNT(
SELECT *
FROM `apartment_booking` AS ab
JOIN apartment AS a ON ( a.id = apartmentId )
LEFT OUTER JOIN booking AS b ON ( b.id = bookingId )
WHERE bedrooms = '4' and checkIn <= <<<SOMEDATEHERE>>> and checkOut >= <<<<SOMEOTHERDATEHERE>>>>>
) >= 4);
Related
How can I join a table to itself having it look like it's three tables?
Honestly, I don't know how to join a table to itself to solve this problem. I have a table that stored it's record in the format below: I want to query the table and display it's record in this format: This is what I have tried so far Select f.score as first_term, s.score as second_term, t.score as term_tetm from table f left join table s left join table t using (studentid) where studentid = 001 group by subject
You can get the result by grouping on the subject name, but then you will have to use an aggregate function. Here is an example: CREATE TABLE #StudentGrades ( [SUBJECT] VARCHAR(50), [STUDENT_ID] VARCHAR(3), [SCORE] INT, [TERM] VARCHAR(50) ) INSERT INTO #StudentGrades ([SUBJECT], [STUDENT_ID],[SCORE],[TERM]) VALUES ('English', '001', 50, '1st_Term'), ('Mathematics', '001', 40, '1st_Term'), ('French', '001', 60, '1st_Term'), ('English', '001', 60, '2nd_Term'), ('Mathematics', '001', 50, '2nd_Term'), ('French', '001', 50, '2nd_Term'), ('Computer', '001', 70, '2nd_Term'), ('English', '001', 65, '3rd_Term'), ('Mathematics', '001', 60, '3rd_Term'), ('French', '001', 70, '3rd_Term'), ('Computer', '001', 80, '3rd_Term') SELECT [SUBJECT], MAX(CASE WHEN [TERM] = '1st_Term' THEN [SCORE] END) AS '1ST_TERM', MAX(CASE WHEN [TERM] = '2nd_Term' THEN [SCORE] END) AS '2ND_TERM', MAX(CASE WHEN [TERM] = '3rd_Term' THEN [SCORE] END) AS '3RD_TERM' FROM #StudentGrades GROUP BY [SUBJECT] ORDER BY [SUBJECT]
This is a simple solution but it works. Selecting student, subject and then sub-select for each of the terms. Finally, we DISTINCT it because there will be repetitions due to same subject on more than 1 term. SELECT DISTINCT student_id, `subject`, (SELECT score FROM test AS t2 WHERE t2.student_id = t1.student_id AND t1.subject=t2.subject AND t2.term=1) AS term1, (SELECT score FROM test AS t2 WHERE t2.student_id = t1.student_id AND t1.subject=t2.subject AND t2.term=2) AS term2, (SELECT score FROM test AS t2 WHERE t2.student_id = t1.student_id AND t1.subject=t2.subject AND t2.term=3) AS term3 FROM test AS t1
Filter products by price
I am trying to filter product by price but ran into a problem with special price. If the special price is applicable in product than below query is showing random results. "SELECT * FROM tablename WHERE ((price >= ".(int)$min_price." AND price <= ".(int)$max_price." AND ('".date('Y-m-d')."' NOT BETWEEN special_price_startdate AND special_price_enddate OR special_price_startdate = NULL OR special_price_enddate= NULL)) OR (('".date('Y-m-d')."' BETWEEN special_price_startdate AND special_price_enddate AND special_price_startdate IS NOT NULL AND special_price_enddate IS NOT NULL) AND special_price >= ".(int)$min_price." AND special_price <= ".(int)$max_price.")) AND isactive = 1 AND isdeleted = 0 ORDER BY created DESC, productid DESC LIMIT ".(($page-1)*$perpage).",".$perpage;
The query does exactly what it should do according to its syntax: if special price is applicable and special price is between set limit, it returns the row. Otherwise, if the special price is not applicable and regular price is between the set limits it returns the row: CREATE TABLE `tablename` ( `id` int(11) NOT NULL AUTO_INCREMENT, `price` int(11) DEFAULT NULL, `special_price_startdate` date DEFAULT NULL, `special_price_enddate` date DEFAULT NULL, `isactive` bit(1) DEFAULT NULL, `isdeleted` bit(1) DEFAULT NULL, `special_price` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; Query adjusted for PHP inputs: SELECT * FROM tablename WHERE ( ( price >= 99 AND price <= 999 AND ( '2017-04-21' NOT BETWEEN special_price_startdate AND special_price_enddate OR special_price_startdate = NULL OR special_price_enddate = NULL)) OR ( ( '2017-04-21' BETWEEN special_price_startdate AND special_price_enddate AND special_price_startdate IS NOT NULL AND special_price_enddate IS NOT NULL) AND special_price >= 99 AND special_price <= 999)) AND isactive = 1 AND isdeleted = 0 /*ORDER BY created DESC, productid DESC*/ LIMIT 10; Insert seed data: INSERT INTO `tablename` values (1, 120, '2017-04-20', '2017-04-22', 1, 0, 125), (2, 125, '2017-04-27', '2017-04-28', 1, 0, 125), (3, 120, '2017-04-20', '2017-04-22', 1, 0, 50), (4, 50, '2017-04-27', '2017-04-28', 1, 0, 125) Output: 1 120 4/20/2017 12:00:00 AM 4/22/2017 12:00:00 AM 1 0 125 2 125 4/27/2017 12:00:00 AM 4/28/2017 12:00:00 AM 1 0 125
How to join with multiple tables in MySQL?
The image of the table relation can be found at image. -- Table structure for table `area` CREATE TABLE `area` ( `area_id` int(10) NOT NULL auto_increment, `area_name` varchar(255) NOT NULL, PRIMARY KEY (`area_id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ; INSERT INTO `area` (`area_id`, `area_name`) VALUES (1, 'Area 1'), (2, 'Area 2'), (3, 'Area 3'), (4, 'Area 4'); -- Table structure for table `fruits` CREATE TABLE `fruits` ( `fruit_id` int(10) NOT NULL auto_increment, `fruit_name` varchar(255) NOT NULL, `area_id` int(10) NOT NULL, PRIMARY KEY (`fruit_id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ; INSERT INTO `fruits` (`fruit_id`, `fruit_name`, `area_id`) VALUES (1, 'Apple', 1), (2, 'Orange', 1), (3, 'Mango', 2), (4, 'Apricot', 3); -- Table structure for table `vegetables` CREATE TABLE `vegetables` ( `veg_id` int(10) NOT NULL auto_increment, `veg_name` varchar(255) NOT NULL, `area_id` int(10) NOT NULL, PRIMARY KEY (`veg_id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ; INSERT INTO `vegetables` (`veg_id`, `veg_name`, `area_id`) VALUES (1, 'Chickpea', 1), (2, 'Drumstick', 4); If I use the following query I get the output as below SELECT `area`.`area_name` AS AreaName ,COUNT(*) AS num FROM `area` INNER JOIN `fruits` ON (`fruits`.`area_id` = `area`.`area_id`) GROUP BY `fruits`.area_id UNION ALL SELECT `area`.`area_name` AS AreaName ,COUNT(*) AS num FROM `area` INNER JOIN `vegetables` ON (`vegetables` .`area_id` = `area`.`area_id`) GROUP BY `vegetables`.area_id AreaName num Area 1 2 Area 2 1 Area 3 1 Area 1 1 Area 4 1 But I want the output to be like: it should fetch all the areas which are present in vegetables and fruits and if the area is repeating in either fruits or vegetables it should return the total count of area_id by totalling the count of fruits and vegetables.. so the output will be like below AreaName num Area 1 3 Area 2 1 Area 3 1 Area 4 1
You can use a subselect over your query and use SUM() to add the counts for same area SELECT t.AreaName ,SUM(t.num) num FROM ( ....) t GROUP BY t.AreaName Fiddle Demo
MySQL Left Join with WHERE and Greater than
I want to do a LEFT JOIN and then sort my display by a rank column and also show items only if the qty column is greater than 0 SELECT * FROM `product` LEFT JOIN `stock` ON `product`.`product_id`=`stock`.`pid` AND `qty` > 1 ORDER BY `product`.`rank` ASC Product Table product_id name rank price Stock Table pid price_sale qty Product Table 12, Pen, 2, 53.00 13, Pen, 1, 58.00 14, Pen, 3, 25.00 Stock Table 12, 10.00, 5 13, 18.00, 15 My results do not appear. It should display: 12, Pen, 2, 53.00, 12, 10.00, 5 13, Pen, 1, 58.00, 13, 18.00, 15
Use Below Query SELECT * FROM `product` LEFT JOIN `stock` ON `product`.`product_id`=`stock`.`pid` WHERE `stock`.`qty` > 0 ORDER BY `product`.`rank` ASC
The AND should be a WHERE like this : SELECT * FROM `product` LEFT JOIN `stock` ON `product`.`product_id`=`stock`.`pid` WHERE `qty` > 1 ORDER BY `product`.`rank` ASC
Why am I getting wrong values from my joined tables?
Here is my MySQL schema CREATE TABLE IF NOT EXISTS `sales` ( `id` int(11) NOT NULL AUTO_INCREMENT, `date` date NOT NULL, `total_tax` decimal(25,2) NOT NULL, `total` decimal(25,2) NOT NULL, `total_tax2` decimal(25,2) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=38 ; INSERT INTO `sales` (`id`, `date`, `total_tax`, `total`, `total_tax2`) VALUES (1, '2013-02-14', 6, 100, 21), (2, '2013-02-18', 6, 100, 21), (3, '2013-03-01', 6, 100, 21), (4, '2013-03-07', 6, 100, 21), (5, '2013-03-28', 6, 100, 21), (6, '2013-03-28', 6, 100, 21), (7, '2013-04-04', 6, 100, 21); CREATE TABLE IF NOT EXISTS `purchases` ( `id` int(11) NOT NULL AUTO_INCREMENT, `date` date NOT NULL, `total` decimal(25,2) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=15 ; INSERT INTO `purchases` (`id`, `date`, `total`) VALUES (1, '2013-02-15', 150), (2, '2013-02-16', 18), (3, '2013-03-05', 80), (4, '2013-03-09', 50), (5, '2013-03-16', 500), (6, '2013-03-22', 200); And I am trying to get the total values of sales.total, total_tax, total_tax2 and purchases.total by month by joining table on date. I am trying this SQL query: SELECT date_format( sales.date, '%b %Y' ) AS MONTH, SUM( COALESCE( sales.total, 0 ) ) AS sales, SUM( COALESCE( purchases.total, 0 ) ) AS purchases, SUM( COALESCE( total_tax, 0 ) ) AS tax1, SUM( COALESCE( sales.total_tax2, 0 ) ) AS tax2 FROM sales LEFT JOIN purchases ON date_format(purchases.date, '%b %Y' ) = date_format(sales.date, '%b %Y' ) WHERE sales.date >= date_sub( now( ) , INTERVAL 12 MONTH ) GROUP BY date_format( purchases.date, '%b %Y' ) ORDER BY date_format( sales.date, '%m' ) ASC and getting results as Feb 2013 400 336 24 84 Mar 2013 1600 3320 96 336 Apr 2013 100 0 6 21 Why am I getting these incorrect values? SQL Fiddle
You are joining by the month of the dates of your tables, meaning that you are multiplying the rows that you are suming. You either perform the aggregation previously or join by the key of your tables: SELECT S.Month, S.sales, ISNULL(P.purchases,0) purchases, S.tax1, S.tax2 FROM ( SELECT date_format(date, '%b %Y') Month, SUM(total) Sales, SUM(total_tax) tax1, SUM(total_tax2) tax2 FROM sales WHERE sales.date >= date_sub( now( ) , INTERVAL 12 MONTH ) GROUP BY date_format(date, '%b %Y')) S LEFT JOIN ( SELECT date_format(date, '%b %Y') Month, SUM(total) purchases FROM purchases GROUP BY date_format(date, '%b %Y')) P ON S.Month = P.Month GROUP BY S.Month ORDER BY S.Month Here is the sqlfiddle with this option.