Need help with SQL query

  • Hi All,

    I'm pretty new to this and am in need of some help writing a query. I'll try to explain.

    I have 2 tables

    1. tbl_accom

    2. tbl_bookings

    The table tbl_accom has 3 columns accomID which is the PK, accomname (accommdation name such as single bedroom and double bedroom) and availno is an integer for the amount of room available.

    The table tbl_bookings includes all the guests booking details but the 4 columns I want to concentrate on are book_ID which is the PK, arrivedate, departdate and accomID which is the FK.

    What i am trying to achieve is to write a query that pulls out all of the available accommodation that has a availabilty greater than 0 between 2 given dates.

    Below is what I have at the moment:

    Select accomname, tbl_accom.availno - count(book_id) as availability

    From tbl_accom

    Left Outer Join tbl_bookings

    on tbl_bookings.accomID = tbl_accom.accomID

    where tbl_bookings.arrivedate between '2016-09-12' and '2016-10-12' or

    tbl_bookings.arrivedate between '2016-09-12' and '2016-10-12' and tbl_bookings.departdate between '2016-01-12' and '2016-10-12' or

    tbl_bookings.departdate between '2016-09-12' and '2016-10-12'

    Group By tbl_accom.accomname, tbl_accom.availno

    The dates I have used just as an example is 2016-09-12 to 2016-10-12 but all the query is pulling out is the 1 record and from what I have read, this is to do with the Where clause. However I don't know how to change this.

    Any help at all would be greatly appreciated.

    Thanks,

  • Firstly a brief explanation of how AND and OR statements work. I noticed that you have a mixture of AND and OR without any brackets, which is likely causing some logic concerns.

    Your code shows it has following logic:

    --I've tabbed below intentionally for any that find it "offensive to mine eyes" ;)

    tbl_bookings.arrivedate between '2016-09-12' and '2016-10-12'

    OR

    tbl_bookings.arrivedate between '2016-09-12' and '2016-10-12'

    AND

    tbl_bookings.departdate between '2016-01-12' and '2016-10-12'

    OR

    tbl_bookings.departdate between '2016-09-12' and '2016-10-12'

    You should really be encapsulating your OR statements. It can mean if someone moves something around it doesn't after your query, as you can tell where items are linked.

    I'm kind of guessing at an end result here, however, does this fulfil your needs?

    SELECT accomname, --Where is this from?? Always use aliasing

    a.availno - count(b.book_id /*I guessed this is from booking*/) as [Availability] --Try to avoid key words for Column names too

    FROM tbl_accom a --prefixes like this are generally frowned upon as well, due to possible system database conflicts

    LEFT OUTER JOIN tbl_bookings b ON a.accomID = b.accomID

    WHERE (b.arrivedate BETWEEN '2016-09-12' AND '2016-10-12'

    OR b.arrivedate BETWEEN '2016-09-12' AND '2016-10-12')

    AND (b.departdate BETWEEN '2016-01-12' AND '2016-10-12'

    OR b.departdate BETWEEN '2016-09-12' AND '2016-10-12')

    GROUP BY a.accomname,

    a.availno

    HAVING a.availno - count(b.book_id) > 0;

    EDIT: I really need to fix my spacebar.

    Thom~

    Excuse my typos and sometimes awful grammar. My fingers work faster than my brain does.
    Larnu.uk

  • Thom A (12/26/2016)


    Firstly a brief explanation of how AND and OR statements work. I noticed that you have a mixture of AND and OR without any brackets, which is likely causing some logic concerns.

    Your code shows it has following logic:

    --I've tabbed below intentionally for any that find it "offensive to mine eyes" ;)

    tbl_bookings.arrivedate between '2016-09-12' and '2016-10-12'

    OR

    tbl_bookings.arrivedate between '2016-09-12' and '2016-10-12'

    AND

    tbl_bookings.departdate between '2016-01-12' and '2016-10-12'

    OR

    tbl_bookings.departdate between '2016-09-12' and '2016-10-12'

    You should really be encapsulating your OR statements. It can mean if someone moves something around it doesn't after your query, as you can tell where items are linked.

    I'm kind of guessing at an end result here, however, does this fulfil your needs?

    SELECT accomname, --Where is this from?? Always use aliasing

    a.availno - count(b.book_id /*I guessed this is from booking*/) as [Availability] --Try to avoid key words for Column names too

    FROM tbl_accom a --prefixes like this are generally frowned upon as well, due to possible system database conflicts

    LEFT OUTER JOIN tbl_bookings b ON a.accomID = b.accomID

    WHERE (b.arrivedate BETWEEN '2016-09-12' AND '2016-10-12'

    OR b.arrivedate BETWEEN '2016-09-12' AND '2016-10-12')

    AND (b.departdate BETWEEN '2016-01-12' AND '2016-10-12'

    OR b.departdate BETWEEN '2016-09-12' AND '2016-10-12')

    GROUP BY a.accomname,

    a.availno

    HAVING a.availno - count(b.book_id) > 0;

    EDIT: I really need to fix my spacebar.

    I really don't think that your rewrite is what the OP is looking for, because in both subclauses you're ORing two identical predicates. I think the OP meant to use the logical precedence that ANDs are evaluated before ORs.

    For the OP, this sounds like a gaps and islands problem, but the problem is that you're looking at the gaps (booked) rather than the islands (availability). I assume that you want to be able to find contiguous availability rather than just total availability, and your setup is not conducive to that approach.

    Also, you're running into the typical problem with identifying overlapping intervals: you're trying to compare starts with starts and ends with ends. The formula is actually quite simple: each interval starts before the other ends. In code that's

    b.arrivedate <= '2016-10-12' AND

    b.departdate > '2016-09-12'

    The approach I would take is use a calendar table to create all possible dates for each accommodation between the start and end dates, then join on your bookings table, and, finally, find which dates didn't have any bookings.

    Drew

    J. Drew Allen
    Business Intelligence Analyst
    Philadelphia, PA

  • The table tbl_bookings is outer joined. Thus, every reference to columns from that table must allow for the column to be NULL. The WHERE clause references to tbl_bookings must allow for NULLs or be included in the ON clause. In the query below, I have added them to the ON clause and removed the WHERE clause.

    SELECT A.accomname, A.availno - count(B.book_id) AS [availability]

    FROM tbl_accom A

    LEFT OUTER JOIN tbl_bookings B

    ON B.accomID = A.accomID

    AND B.arrivedate BETWEEN '2016-09-12' and '2016-10-12' OR

    B.arrivedate BETWEEN '2016-09-12' and '2016-10-12' AND B.departdate BETWEEN '2016-01-12' AND '2016-10-12' OR

    B.departdate BETWEEN '2016-09-12' and '2016-10-12'

    GROUP BY A.accomname, A.availno

    This fix is in addition to the other issues pointed out by the other responses (using table aliases, grouping the OR/AND clauses, using correct criteria for date ranges).

  • Hi Guys,

    Thanks for the replies, after a bit of playing about I finally got it to work as I needed. I used the below code.

    <code>

    SELECT a.accomname, a.availno - count(book_id) as availability

    FROM tbl_accom AS a

    LEFT OUTER JOIN tbl_bookings AS b

    on b.accomID = a.accomID

    and (b.arrivedate between '2016-01-11' and '2016-09-12' or b.departdate between '2016-01-11' and '2016-09-12')

    GROUP BY a.accomname, a.availno

    </code>

    Just a quick question, the dates in the code will be populated from calendars on my website, how can I replace the dates with variables? Then all I have to do is put the value into the variable in vb.net.

    Cheers,

  • Simon Hammill (12/27/2016)


    Hi Guys,

    Thanks for the replies, after a bit of playing about I finally got it to work as I needed. I used the below code.

    SELECT a.accomname, a.availno - count(book_id) as availability

    FROM tbl_accom AS a

    LEFT OUTER JOIN tbl_bookings AS b

    on b.accomID = a.accomID

    and (b.arrivedate between '2016-01-11' and '2016-09-12' or b.departdate between '2016-01-11' and '2016-09-12')

    GROUP BY a.accomname, a.availno

    Just a quick question, the dates in the code will be populated from calendars on my website, how can I replace the dates with variables? Then all I have to do is put the value into the variable in vb.net.

    Cheers,

    Again, this can be simplified.

    SELECT a.accomname, a.availno - count(book_id) as availability

    FROM tbl_accom AS a

    LEFT OUTER JOIN tbl_bookings AS b

    on b.accomID = a.accomID

    and b.arrivedate <= '2016-09-12' -- or @end_range

    and b.departdate >= '2016-01-11' -- or @start_range

    GROUP BY a.accomname, a.availno

    Drew

    PS: I replaced the incorrect code tags with the correct ones. See my signature for more details.

    J. Drew Allen
    Business Intelligence Analyst
    Philadelphia, PA

  • >> I'm pretty new to this and am in need of some help writing a query. I'll try to explain. <<

    Do not try to explain things by telling us a story. If you had read the forum rules, you would post DDL. That way we do not have to try and guess at everything, do transcriptions for you, etc.

    Another classic mistake is tibbling. This term was invented by Phil Factor; t refers to using metadata affixes in schema element names (like “tbl_”). According to ISO 11179 standards and basic data modeling. You should name a table for what it is by its very nature, not physical storage, or language constructs.

    CREATE TABLE Accommodations

    (accommodation_id CHAR(5) NOT NULL PRIMARY KEY,

    accommodation_type CHAR(5) NOT NULL

    CHECK (accommodation_type IN (‘1BR’, ‘2BR’, ..)),

    occupied_room_count INTEGER DEFAULT 0 NOT NULL

    CHECK(occupied_room_count >= 0));

    What you were calling a name is actually a type. Why do you think that “single bedroom” is a name? Is there only one of them in all the world?

    CREATE TABLE Bookings

    (booking_nbr CHAR(16) NOT NULL PRIMARY KEY,

    arrival_date DATE DEFAULT CURRENT_TIMESTAMP NOT NULL,

    departure_date DATE,

    CHECK (arrival_date <= departure_date)

    accommodation_id CHAR(5) NOT NULL

    REFERENCES Accommodations (accommodation_id)

    ON DELETE CASCADE

    ON UPDATE CASCADE

    );

    See how check constraints and references do so much of your programming declaratively. A NULL departure date indicates that the accommodation is still booked. Why not make this a procedure and use parameters instead of constant dates?

    >> a query that pulls out all of the available accommodation that have availability greater than 0 BETWEEN two given dates. Below is what I have at the moment:

    You do not seem to understand how Boolean algebra works; the AND is stronger than the OR. However, the [NOT] BETWEEN..AND 3-ary predicate is stronger than both of them.

    Let us try a little different approach. We are looking for a row in the bookings that has the (@start_date,@end_date) interval inside it.

    SELECT B.accommodation_id

    FROM B.Bookings AS B

    WHERE B.arrival_date <= @start_date

    AND B.departure_date >= @end_date

    GROUP BY B.accommodation_id

    HAVING SUM(B.occupied_room_count) = 0);

    This interval should have no occupied rooms. This gives you the accommodation ID and then you can join it back the accommodations table, if you need more details.

    Books in Celko Series for Morgan-Kaufmann Publishing
    Analytics and OLAP in SQL
    Data and Databases: Concepts in Practice
    Data, Measurements and Standards in SQL
    SQL for Smarties
    SQL Programming Style
    SQL Puzzles and Answers
    Thinking in Sets
    Trees and Hierarchies in SQL

Viewing 7 posts - 1 through 6 (of 6 total)

You must be logged in to reply to this topic. Login to reply