Home > Software engineering >  Access: WHERE IN AND NOT IN on same column
Access: WHERE IN AND NOT IN on same column

Time:01-20

Let's say I have a table animal and a table competition with 1:n relationship.

Table animal looks like:

id_animal type
1 cat
2 dog

Table competition looks like:

id_competition fid_animal name
1 1 A
2 2 A
3 2 B

This means the cat is participating in competition A and the dog is participating in competition A and B.

I now want to filter the table animal based on the entries in table competition.

The query sounds like:

Show me all animals (type), that participate in competition (name) A but not B. The result should give me only the cat, not the dog.

I tried my luck with following sql (simplified):

Select DISTINCT * FROM animal 
LEFT JOIN competition ON id_animal = fid_animal 
WHERE competition.name IN ("A") AND NOT competition.name IN ("B");

As result I get both, cat and dog. Only AND NOT returns me the dog, which is fine but both together don't give me the desired result.

What am I doing wrong? How can I achieve the output?

Thank you in advance!

Background: I have a bound form (datatable) with lot of entries and a table as data source. The user should be able to filter the form based on parameters in a related (1:n) table. So I made an unbound form with listboxes (populated with possible entries of the related table), where the user can choose multiple criteria. The result is a sql statement like above which is used as new data source for the bound form. This logic is working fine. Problem is that sometimes an entry in the form can have multiple parameters and for the user it is important to filter by chosen parameters but exclude the result if it contains specific other parameters. There are a few related tables, many listboxes are part of the filter and the sql string is generated dynamically, based on if and what the user selects.

CodePudding user response:

Consider:

SELECT animal.* FROM animal LEFT JOIN competition ON id_animal = fid_animal 
WHERE [name]="A" AND NOT id_animal IN (SELECT fid_animal FROM competition WHERE [name]="B")

Might find this related question of interest Microsoft Access - Filtering a form based on check boxes for one field

CodePudding user response:

You need to look at several rows. Your predicate:

WHERE competition.name IN ("A") AND NOT competition.name IN ("B");

is evaluated against the same row. It will find all rows where name is 'A' and not 'B', i.e. competition 1 and 2. If you instead look at the rows with name 'A' where it does not exists a row for the same animal with name 'B' you will find what you are looking for:

SELECT c1.fid_animal
FROM competition c1
WHERE c1.name = 'A'
  AND NOT EXISTS (
      SELECT * 
      FROM competition c2
      WHERE c1.fid_animal = c2.fid_animal
        AND c2.name = 'B'
) 

Now you can join this result with animal to find the type:

SELECT a.type
FROM competition c1

JOIN animal a
    ON a.id_animal = c1.fid_animal

WHERE c1.name = 'A'
  AND NOT EXISTS (
      SELECT * 
      FROM competition c2
      WHERE c1.fid_animal = c2.fid_animal
        AND c2.name = 'B'
);

FWIW, type is a reserved word in SQL so you should avoid using that as an identifier.

CodePudding user response:

MS Access is usually far from standard compliant. The following is the simple query in standard SQL and runs in about every RDBMS:

SELECT *
FROM animal 
WHERE id_animal IN (SELECT id_animal FROM competition WHERE name = 'A')
  AND id_animal NOT IN (SELECT id_animal FROM competition WHERE name = 'B');

[NOT] IN works on lists. It makes little sense to use it on single-item lists. name IN ("A") is the same as name = "A". As you want to look up the animal list in the competition table, you need a subquery as shown above.

As you see, I don't join. Why should I? I want to select animals, so I select from the animal table. I want to restrict the result to certain animals, so I use a WHERE clause.

I notice you are using double quotes for string literals. If this is necessary in MS Access, then replace the single quotes above with double quotes. (In standard SQL double quotes delimit names, not string literals, but this may be different in MS Access.)

  •  Tags:  
  • Related