views:

1286

answers:

7

I have a database which is in Access (you can get it link text). If I run

SELECT DISTINCT Spl.Spl_No, Spl.Spl_Name
FROM Spl INNER JOIN Del 
   ON Spl.Spl_No = Del.Spl_No
WHERE Del.Item_Name <> 'Compass'

It provides the names of the suppliers that have never delivered a compass. However you can supposedly do this with a sub-query. So far myself and a few others have not been able to get it right.

I did come close with the following, until we added more suppliers then it stopped working

SELECT SPL.SPL_Name
FROM SPL
LEFT JOIN DEL ON Del.SPL_No = SPL.SPL_No
WHERE (DEL.Item_Name<>"Compass") OR (DEL.Item_Name IS NULL)
GROUP BY SPL.SPL_Name
HAVING COUNT(DEL.SPL_No) = 0

So the question: Is this possible to do with a sub-query.

A: 

Suppliers that have never delivered a compass:

SELECT Spl.Spl_No, Spl.Spl_Name
FROM Spl
LEFT JOIN Del ON Del.Spl_No = Spl.Spl_No AND Del.Item_Name = 'Compass'
WHERE Del.Item_Name IS NULL

and using a sub query:

SELECT Spl_No, Spl_Name
FROM Spl
WHERE Spl_No IN 
  (
    SELECT Spl_No
    FROM Del
    GROUP BY Spl_No, Item_Name
    WHERE Item_Name = 'Compass'
    HAVING COUNT(*) = 0
  )
Joel Coehoorn
The subquery is too complex. The optimizer may decide to optimize it to a more simple IN or EXISTS, but probably not. Anyway, it's syntactically complex, too, for no real reason. Why make it harder for a person to parse?
Matt Rogish
That's a feature, not a bug ;) I want to steer him to using the JOIN version.
Joel Coehoorn
The join version is weird - those suppliers where item_name is compass where item_name is null; yes, it's correct, but it is plain weird.
Jonathan Leffler
Doesn't work with Access - Seems the WHERE and GROUP clauses are the wrong way around.Also if you swop them you get nothing back.
Robert MacLean
The join version is the correct approach for Access, though. Performance goes to utter hell in Access when you use IN, NOT IN, EXISTS, etc. You need to do LEFT JOINs and test for NULL, ugly though it may be.
Tmdean
IN utilizes indexes better than NOT IN. I don't use EXISTS much, but when I've tried it, I found it less efficient with indexes than IN. Joins are better optimized in Jet than subqueries, whether explicit joins or implicit (i.e., using a WHERE clause instead of a JOIN statement).
David-W-Fenton
A: 

Is this what you're looking for?

SELECT
   SPL.SPL_name
FROM
   SPL
WHERE
   SPL.SPL_No NOT IN
   (
      SELECT SPL_No FROM DEL WHERE DEL.Item_Name <> "Compass"
   )
brian
You don't want <> in the subquery
Matt Rogish
Agree with Matt - you'd get an up-vote if it was equal.
Jonathan Leffler
+1  A: 
SELECT Spl_No
     , Spl_Name
  FROM Spl
 WHERE NOT EXISTS( SELECT *
                     FROM Del
                    WHERE Del.Spl_no = Spl.Spl_no
                      AND Item_name  = 'Compass' )
Matt Rogish
+2  A: 

Do you mean something like this?

SELECT SPL.SPL_Name
FROM SPL
WHERE NOT SPL.SPL_no IN 
 (SELECT SPL_no FROM DEL WHERE DEL.Item_Name = "Compass")
Dan Sydner
+1  A: 

If Access has Exists.....

SELECT SPL.SPL_Name FROM SPL WHERE NOT EXISTS (SELECT 1 FROM DEL WHERE Del.SPL_No = SPL.SPL_No AND (DEL.Item_Name='Compass') )

WIDBA
Jet SQL supports EXISTS, but it is not well-optimized in all cases for utilizing indexes. IN seems to me to utilize indexes more fully, though NOT IN does not. Go figure.
David-W-Fenton
+1  A: 

It's pretty close to exactly how you would say it in English

"Give me the suppliers who have not made a delivery of Compasses."

Select [Stuff]
From Spl S
Where Not Exists
   (Select * From Del
    Where Spl_no = S.Spl_no 
       And Item_name  = 'Compass')

EDIT: Without Exists, you can use Count(*) = 0

Select [Stuff]
From Spl S
Where 
   (Select Count(*) From Del
    Where Spl_no = S.Spl_no 
       And Item_name  = 'Compass') = 0
Charles Bretana
+3  A: 

I think I would go for:

SELECT SELECT Spl_No, Spl_Name
FROM Spl
WHERE Spl_No NOT IN 
  (SELECT Spl_No FROM Del
   WHERE Item_Name = 'Compass')
Remou