Question

I have three tables, one that lists regions, one that lists hotels and another that lists reservations. The hotels table has a foreign key for the regions table, to show which hotel is within which region, and the reservation table has a foreign key for the hotels table, to list reservations made against a hotel.

I'm trying to make it so that you can input a date, and the hotel isn't returned in the results, if that date is unavailable (though it's also possible to NOT input a date).

Currently, using the query listed at the bottom of the page, it lists out all the hotels even if they don't have a reservation against them. At this time, only one hotel has any reservations, and at that, there are two reservations in total. Before I included the 'DISTINCT' clause, to make it so that duplicate entries aren't listed, it would list out all hotels, including the one with reservations, except this one it would list TWICE as there were TWO reservations. Now, with the DISTINCT clause, it will only ever show one result, but it will ALWAYS show one result. It essentially says "This entry is in the time range specified, so we can't show that, but the other one isn't, so we will show it", even though it's the same hotel.

For reference, my query currently, using 2020-09-02 as the date to check availability for;

SELECT DISTINCT hotels.hotel_name FROM hotels

LEFT JOIN reservations ON hotels.id=reservations.hotel_id

LEFT JOIN regions ON hotels.region_id=regions.id WHERE Cast('2020-09-02' as DateTime) NOT BETWEEN reservations.start_date AND reservations.end_date OR reservations.start_date is NULL OR reservations.end_date is NULL;

Current results (Where date IS available);

Hotel 1 with two reservations
Hotel 2
Hotel 3
Hotel 4
Hotel 5

Current results (Where date ISN'T available);

Hotel 1 with two bookings
Hotel 2
Hotel 3
Hotel 4
Hotel 5

Current results (Where date is available, but 'DISTINCT' ISN'T used);

Hotel 1 with two bookings
Hotel 1 with two bookings
Hotel 2
Hotel 3
Hotel 4
Hotel 5

Current results (Where date isn't available, but 'DISTINCT' ISN'T used);

Hotel 1 with two bookings
Hotel 2
Hotel 3
Hotel 4
Hotel 5

Expected results (If chosen date ISN'T available);

Hotel 2
Hotel 3
Hotel 4
Hotel 5

Expected results (If chosen date IS available);

Hotel 1 with two bookings
Hotel 2
Hotel 3
Hotel 4
Hotel 5

Hopefully the expected results scenarios give an idea of what I'm going for.

Any help is appreciated.

EDIT: Link to a DBFiddle of the issue here: https://www.db-fiddle.com/f/n9Ew6nLCWQHDjYfxzp49xB/16

Was it helpful?

Solution

You need a DISTINCT because of the n:m relationships But the date selection i would write differently, so that all holtel.id should be excluded, which have a reservation on that day, so that it can't be choosen anymore

Schema (MySQL v5.7)

CREATE TABLE `reservations` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `hotel_id` int(10) unsigned DEFAULT NULL,
  `start_date` date DEFAULT NULL,
  `end_date` date DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;


insert  into `reservations`(`id`,`hotel_id`,`start_date`,`end_date`) values
    (1,1,'2020-08-26','2020-09-02'),
    (2,1,'2020-12-06','2020-12-13');


CREATE TABLE `regions` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `region_name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

insert  into `regions`(`id`,`region_name`) values
    (1,'Region 1'),
    (2,'Region 2'),
    (3,'Region 3'),
    (4,'Region 4'),
    (5,'Region 5');


CREATE TABLE `hotels` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `region_id` int(10) unsigned DEFAULT NULL,
  `hotel_name` varchar(255) DEFAULT NULL,

  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

insert  into `hotels`(`id`,`region_id`,`hotel_name`) values
    (1,1,'Hotel 1'),
    (2,3,'Hotel 2'),
    (3,5,'Hotel 3'),
    (4,5,'Hotel 4'),
    (5,4,'Hotel 5');

Query #1

SELECT 
    DISTINCT hotels.hotel_name, regions.region_name
FROM
    hotels
        LEFT JOIN
    reservations ON hotels.id = reservations.hotel_id
        LEFT JOIN
    regions ON hotels.region_id = regions.id
WHERE
(reservations.start_date is NULL OR reservations.end_date is NULL)

        OR hotels.id NOT IN
        (SELECT h.id 
FROM hotels h
    INNER JOIN 
    reservations r ON h.id = r.hotel_id
WHERE
   CAST('2020-09-02' AS DATE) BETWEEN r.start_date AND r.end_date)  
;
hotel_name region_name
Hotel 2 Region 3
Hotel 3 Region 5
Hotel 4 Region 5
Hotel 5 Region 4

View on DB Fiddle

Licensed under: CC-BY-SA with attribution
Not affiliated with dba.stackexchange
scroll top