There are three common approaches. Here is a LEFT JOIN approach:
SELECT T1.CustID
FROM yourtable T1
LEFT JOIN yourtable T2 ON T1.CustID = T2.CustID AND T2.ProdId = 300
WHERE T1.ProdId = 105
AND T2.ProdId IS NULL
Here is NOT IN:
SELECT CustID
FROM yourtable
WHERE ProdId = 105
AND CustID NOT IN
(
SELECT CustID
FROM yourtable
WHERE ProdId = 300
)
Or you could use NOT EXISTS:
SELECT CustID
FROM yourtable T1
WHERE ProdId = 105
AND NOT EXISTS
(
SELECT NULL
FROM yourtable T2
WHERE T2.ProdId = 300
AND T2.CustID = T1.CustID
)
Which has better performance depends on which database and version you are using.
For SQL Server the best is to use either NOT IN or NOT EXISTS:
In SQL Server, NOT EXISTS and NOT IN predicates are the best way to search for missing values, as long as both columns in question are NOT NULL. They produce the safe efficient plans with some kind of an Anti Join.
LEFT JOIN / IS NULL is less efficient, since it makes no attempt to skip the already matched values in the right table, returning all results and filtering them out instead.
Source: