tags:

views:

2655

answers:

6

I am sure making a silly mistake but I can't figure what:

In SQL Server 2005 I am trying select all customers except those who have made a reservation before 2 AM.

When I run this query:

SELECT idCustomer FROM reservations 
WHERE idCustomer NOT IN 
  (SELECT distinct idCustomer FROM reservations 
   WHERE DATEPART ( hour, insertDate) < 2)

I get 0 results.

But

SELECT idCustomer FROM reservations

returns 152.000 results and the "NOT IN" part:

SELECT distinct idCustomer FROM reservations 
WHERE DATEPART ( hour, insertDate) < 2

returns only 284 rows

+8  A: 
SELECT distinct idCustomer FROM reservations
WHERE DATEPART ( hour, insertDate) < 2
  and idCustomer is not null

Make sure your list parameter does not contain null values.

Here's an explanation:

WHERE field1 NOT IN (1, 2, 3, null)

is the same as:

WHERE NOT (field1 = 1 OR field1 = 2 OR field1 = 3 OR field1 = null)
  • That last comparision evaluates to null.
  • That null is OR'd with the rest of the boolean expression, yielding null. (*)
  • null is negated, yeilding null.
  • null is not true - the where clause only keeps true rows, so all rows are filtered.

(*) Edit: this explanation is pretty good, but I wish to address one thing to stave off future nit-picking. (TRUE OR NULL) would evaluate to TRUE. This is relevant if field1 = 3, for example. That TRUE value would be negated to FALSE and the row would be filtered.

David B
That was it, thanks! I still don't understand why. Not null values shouldn't be filtered by the null ones, right?
Dawkins
Great explanation David, many thanks!
Dawkins
A: 

Given it's SQL 2005, you can also try this It's similar to Oracle's MINUS command (opposite of UNION)

But I would also suggest adding the DATEPART ( hour, insertDate) column for debug

SELECT idCustomer FROM reservations 
EXCEPT
SELECT idCustomer FROM reservations WHERE DATEPART ( hour, insertDate) < 2
jerryhung
A: 

should there be a date check as well as time?

ballpointpe0n
A: 
SELECT Reservations.idCustomer FROM Reservations (nolock)
LEFT OUTER JOIN @reservations ExcludedReservations (nolock) ON Reservations.idCustomer=ExcludedReservations.idCustomer AND DATEPART(hour, ExcludedReservations.insertDate) < 2
WHERE ExcludedReservations.idCustomer IS NULL AND Reservations.idCustomer IS NOT NULL
GROUP BY Reservations.idCustomer

[Update: Added additional criteria to handle idCustomer being NULL, which was apparently the main issue the original poster had]

Kevin Fairchild
Hi Kevin, the problem was that customerId was nullable and as @David pointed but many thanks anyway!
Dawkins
Ok. Added in the additional criteria just for completeness :-PI don't have the data to try it out, but I'd be curious to see the difference in EXECUTION plans of NOT IN versus the OUTER JOIN.Anyhow, glad your issue is resolved.
Kevin Fairchild
A: 

Sorry if I've missed the point but wouldn't the following do what you want to do on it's own?

SELECT distinct idCustomer FROM reservations WHERE DATEPART ( hour, insertDate) >= 2)

I was thinking the same thing... However, if a customer has two reservation, one before 2Am and one after, yours will include him, and his would not.
James Curran
Ah, that makes sense, my brain's not firing on all cylinders today!
+2  A: 

It's always dangerous to have NULL in the IN list - it often behaves as expected for the IN but not for the NOT IN:

IF 1 NOT IN (1, 2, 3, NULL) PRINT '1 NOT IN (1, 2, 3, NULL)'
IF 1 NOT IN (2, 3, NULL) PRINT '1 NOT IN (2, 3, NULL)'
IF 1 NOT IN (2, 3) PRINT '1 NOT IN (2, 3)' -- Prints
IF 1 IN (1, 2, 3, NULL) PRINT '1 IN (1, 2, 3, NULL)' -- Prints
IF 1 IN (2, 3, NULL) PRINT '1 IN (2, 3, NULL)'
IF 1 IN (2, 3) PRINT '1 IN (2, 3)'
Cade Roux
Good point. However, I've never run into a case where using null in the in-list gave a more correct answer.
David B
Exactly - the problem is when the list is dynamic and you don't know the NULL is there and you take an IN and change it to a NOT IN.
Cade Roux