Reputation: 667
In SQLite, I have a table named Items:
| id | name |
|----|-------|
| 1 | .. |
| 2 | .. |
| 3 | .. |
| 4 | .. |
| 5 | .. |
| 6 | .. |
Each item might have tags associated with it. Tags are stored in another table, and have their own IDs. The association of items and tags is stored in a third table called Associations:
| itemID | tagID |
|--------|--------|
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
| 3 | 4 |
| 3 | 5 |
| 4 | 5 |
As you can see, item 1 has 3 tags, items 2 and 4 have 1 tag each, and item 3 has 2 tags. Items 5 and 6 have no tags, so their IDs do not appear in the Associations table.
Now, I want to retrieve IDs of items having less than, say, 2 tags, including zero tags. If it were only about the Associations table, the task is easy:
SELECT itemID FROM Associations GROUP BY itemID HAVING COUNT(tagID) < 3;
But this will not return IDs of items which do not have tags, as they are absent in the Associations table. So I guess I have to use JOIN somehow. I tried
SELECT id FROM Items INNER JOIN Associations ON Associations.itemID=Items.id GROUP BY itemID HAVING COUNT(Associations.tagID) < 3;
But this seems to do the same as the previous query. Help is appreciated :)
EDIT: So, the answer is just to replace INNER JOIN
with LEFT JOIN
and GROUP BY itemID
with GROUP BY Items.id
.
QUESTION EXTENDED: Now, to tell the secret, the tags (apart from having IDs) fall into different categories, say, red, green and blue (so, e.g., red tag with id 5 and green tag with id 5 are in fact different tags).
| itemID | tagID |tagType|
|--------|--------|-------|
| 1 | 1 | red |
| 1 | 2 | red |
| 1 | 3 | blue |
| 2 | 1 | green |
| 3 | 4 | blue |
| 3 | 5 | red |
| 4 | 5 | blue |
So I need to retrieve IDs of items which have less than N tags of specific type. I thought I could easily solve this by using the Yogesh's answer by adding the WHERE
clause like this:
SELECT i.id AS itemID
FROM Items i LEFT JOIN
Associations a
ON a.itemID = i.id
WHERE a.tagType='red'
GROUP BY i.id
HAVING COUNT(a.tagID) < 3;
But this again fails to return items which do not have tags at all, because they are now filtered out by the WHERE
condition. I think I need something along these lines:
SELECT i.id AS itemID
FROM Items i LEFT JOIN
Associations a
ON a.itemID = i.id
GROUP BY i.id
HAVING COUNT(a.tagID WHERE a.tagType='red') < 3;
But this does not seem to be a valid statement. Help appreciated again; I might "reaccept" the answer.
Upvotes: 0
Views: 128
Reputation: 50163
Do LEFT JOIN
rather than INNER JOIN
:
SELECT i.id AS itemID
FROM Items i LEFT JOIN
Associations a
ON a.itemID = i.id
GROUP BY i.id
HAVING COUNT(a.tagID) < 3;
Upvotes: 1
Reputation: 94939
You can count in a subquery:
select *
from items i
where 2 >
(
select count(*)
from associations a
where a.itemID = i.id
);
This gets you all items with less than 2 associations (i.e. zero or one).
Upvotes: 0