Search code examples
mysqlsqlsubquerycross-join

MySQL Query for dates that have not been picked by a customer


I am using mySQL to build an application where a customer can pick available dates.

I want two queries, one that specifies what slots each customer picked and one the specifies which date has not yet been picked.

The setup

I have a list of time slots in the form of dates

TABLE: timeslots

slot_id | date
1       | 2020-10-01
2       | 2020-10-02
3       | 2020-10-03

I also have a customer table

TABLE: customers

customer_id | name
1           | Anders
2           | Joe
3           | Karen

Each customer can pick whatever date they like which is specified in the customer_timeslot table which has two Foreign Keys.

TABLE: customer_timeslot

customer_id | slot_id
1           | 1
1           | 2
2           | 1
3           | 1

First query all good

The first query is easy enough and gives me the dates Anders has picked.

The query for the dates Anders (cust. 1) picked

SELECT timeslots.date AS Date, customer.name AS Customer FROM timeslots 
JOIN customer_timeslot
USING (slot_id)
JOIN customers
USING (customer_id)
WHERE customers.customer_id = 1

Result query 1

Date       | Customer
2020-10-01 | Anders
2020-10-02 | Anders

The result I want for the second query

I want the date Anders has not picked yet which would look like this

Date       | Customer
2020-10-03 | Anders

What I've tried

I've tried to use LEFT JOIN instead of JOIN..

SELECT timeslots.date AS Date, customer.name AS Customer FROM timeslots 
LEFT JOIN customer_timeslot
USING (slot_id)
JOIN customers
USING (customer_id)
WHERE customers.customer_id = 1

..Which i expected would give me this result but instead gives me the exact same as the INNER JOIN (No NULL to work with)

Date       | Customer
2020-10-01 | Anders
2020-10-02 | Anders
2020-10-03 | NULL

How can i get the desired query? Shouldn't be so complicated I guess but I'm finding myself completely stuck and looking for some help.


Solution

  • You could use not exists:

    select t.*
    from timeslots t
    where not exists (
        select 1
        from customer_timeslot ct
        where ct.customer_id = 1 and ct.slot_id = t.slot_id
    )
    

    This returns the timeslots that customer_id 1 did not pick. You can get the information for all customers at once with a cross join, then not exists:

    select t.date, c.name
    from timeslots t
    cross join customers c
    where not exists (
        select 1
        from customer_timeslot ct
        where ct.customer_id = c.customer_id and ct.slot_id = t.slot_id
    )