tags:

views:

92

answers:

5
Customers            Holidays

id | name            customer_id | start  | end
---+------           ------------+--------+------
 1 | Peter           1           |   5    | 10
 2 | Simon           1           |  15    | 20
 3 | Mary            2           |   5    | 20

What's a working SQL query that gives me all customers without holidays on a specific date? E.g. date=12

  • Peter
  • Mary

Is this even manageable with a simple SQL join, or do I need to use sub-queries?

A: 

I dont think you want to "remove" anything, just select values that overlap your start/end values, right? try this

SELECT
    Name
FROM
    Customers c
    JOIN Holidays h on h.Customer_id = c.ID 
WHERE
    Start >= @Start
    OR End <= @End
Neil N
+6  A: 

First create a query that finds the opposite of what you want: the customers who do have a holiday on that specific date:

SELECT DISTINCT name
FROM Customers
JOIN Holidays
ON id = customer_id
WHERE start <= 12 AND end >= 12

Result:

Simon

Then join this result back to the customer table and select the results where the join fails:

SELECT name
FROM Customers
LEFT JOIN (
    SELECT DISTINCT id
    FROM Customers
    JOIN Holidays
    ON id = customer_id
    WHERE start <= 12 AND end >= 12
) AS T1
ON Customers.id = T1.id
WHERE T1.id IS NULL

Result:

Peter
Mary

Note that a JOIN isn't the only alternative here. You could also use NOT EXISTS, NOT IN or EXCEPT. Since you didn't specify which database, I chose JOIN because it is a portable and efficient way to do it in all the major relational databases.

The test data I used (taken from the question):

CREATE TABLE Holidays (customer_id INT NOT NULL, start INT NOT NULL, end INT NOT NULL);
INSERT INTO Holidays (customer_id, start, end) VALUES
(1, 5, 10),
(1, 15, 20),
(2, 5, 20);

CREATE TABLE Customers (id INT NOT NULL, name NVARCHAR(100) NOT NULL);
INSERT INTO Customers (id, name) VALUES
(1, 'Peter'),
(2, 'Simon'),
(3, 'Mary');
Mark Byers
SQLite, if you wonder, but that may change in the future. The *LEFT JOIN* with *IS NULL* is a neat trick, didn't think of that one.
Georg
+1  A: 

I THINK you are looking for this:

declare @myStart int
declare @myEnd int
SET @myStart = 11
SET @myEnd = 14

SELECT c.name
FROM Customers c
INNER JOIN Holidays h
    ON c.id=h.customer_id
WHERE
(@myStart BETWEEN h.start AND h.end) OR
(@myEnd BETWEEN h.start AND h.end) OR
(@myStart < h.start AND @myEnd > h.end)
Jaxidian
A: 
select
    c.name
from
    Customers c
    left outer join
        Holidays h
    on
        h.customer_id=c.id
where
    h.start <= 11
    and
    h.end >= 14
group by
    c.id
Björn
+2  A: 

You can do like

  // Get customers only from Customers table
  SELECT name FROM Customers 
  WHERE id NOT IN ( // Get the intersection between the two tables
                    SELECT id FROM Customers JOIN 
                    Holidays ON Customers.Id = Holidays.customer_id
                    WHERE Holidays.start=12 AND Holidays.end = 12)
Wonde