Reputation: 651
I have the following tables:
articles: id, title, content
tags: id, tag, tagCategory
tags2articles: id, idTag, idArticle
categories: id, title, someOtherFields
In a page I need to select all the articles that have a number of tags. I'm using this:
SELECT
SQL_CALC_FOUND_ROWS a.*
FROM
articles AS a
JOIN tags2articles AS ta ON a.id=ta.idArticle
JOIN tags AS t ON ta.idTag=t.id
WHERE
t.id IN (12,13,16)
GROUP BY a.id
HAVING
COUNT(DISTINCT t.id)=3
This will select all the articles that have the tags with the IDs 12,13 and 16 and it works fine. But, the selected articles might also have other tags that might only be specific to one or more of them.
And here comes the tricky part: I want to use these tags to make some filters, so I need another query to select all the distinct tags that the articles above have. Something like this:
╔═══════╦══════╦═══════════╦════════════════╗
║ TagID ║ Tag ║ Category ║ SomeOtherField ║
╠═══════╬══════╬═══════════╬════════════════╣
║ id1 ║ tag1 ║ category1 ║ field1 ║
║ id2 ║ tag2 ║ category2 ║ field2 ║
║ id3 ║ tag3 ║ category1 ║ field1 ║
║ id4 ║ tag4 ║ category3 ║ field3 ║
╚═══════╩══════╩═══════════╩════════════════╝
Upvotes: 2
Views: 3284
Reputation: 44250
This may look ugly, but it will probably be faster
SELECT a.*
FROM articles AS a
WHERE 1=1
AND EXISTS (
SELECT *
FROM tags2articles AS ta
JOIN tags AS t ON ta.idTag=t.id
WHERE a.id=ta.idArticle AND t.id = 12
)
AND EXISTS (
SELECT *
FROM tags2articles AS ta
JOIN tags AS t ON ta.idTag=t.id
WHERE a.id=ta.idArticle AND t.id = 13
)
AND EXISTS (
SELECT *
FROM tags2articles AS ta
JOIN tags AS t ON ta.idTag=t.id
WHERE a.id=ta.idArticle AND t.id = 16
)
;
BTW: the joins with tha tags-tables can probably be omitted (given the proper FK constrants) since ta.idTag=t.id
.
Upvotes: 0
Reputation: 1269773
This is a rewrite of Michael's answer, removing extraneous joins:
SELECT DISTINCT t.id, t.tag, c.title AS Category
FROM tags2Articles t2a INNER JOIN
tags t
ON t.id = t2a.idTag INNER JOIN
categories c ON t.tagCategory = c.id inner join
/* Subquery join returns article ids having all 3 tags you filtered */
/* Joining against tags2articles again will get the remaining tags for these articles */
(SELECT t2a.idArticle
FROM tags2articles t2a
WHERE t2a.idTag IN (12,13,16)
GROUP BY t2a.idArticle
HAVING COUNT(DISTINCT t2a.idTag)=3
) asub
ON t2a.idArticle = asub.idArticle
Upvotes: 2
Reputation: 270617
Using a similar query as you already have as a derived table to join against (but without all the a.*
columns), you may do an INNER JOIN
against tags2articles
to get the remaining tags that those article ids have.
This should result in a distinct list of all tags held by any of the matched articles.
SELECT
DISTINCT
t.id,
t.tag,
c.title AS Category
FROM
tags2Articles t2a
INNER JOIN tags t ON t.id = t2a.idTag
INNER JOIN categories c ON t.tagCategory = c.id
/* Subquery join returns article ids having all 3 tags you filtered */
/* Joining against tags2articles again will get the remaining tags for these articles */
INNER JOIN (
SELECT
a.id
FROM
articles AS a
JOIN tags2articles AS ta ON a.id=ta.idArticle
JOIN tags AS tsub ON ta.idTag=tsub.id
WHERE
tsub.id IN (12,13,16)
GROUP BY a.id
HAVING COUNT(DISTINCT tsub.id)=3
) asub ON t2a.idArticle = asub.id
Upvotes: 5