Finding posts with tag1 AND tag2? (using a join-table) Exists / Having / subqueries… What to use?

限于喜欢 提交于 2019-12-02 20:08:50

问题


I need to make a query to search and filter on multiple terms.

I have a table with weapons and all can have multiple tags. I want to be able to create a filter option for the user that shows only weapons with e.g. 'tag1' AND 'tag2'.

Until now:
I was filtering them using a GROUP_CONCAT with HAVING until now but this has performance issues! As I am forced to do a GROUP BY w.id But I want to do ORDER BY something else. And GROUP BY doesn't play nicely with ORDER BY....

I tried to create something with EXISTS (SELECT …) to be able to filter on multiple values, however I’m not sure to do this when there is a “join-table” in the middle… (so with 2 left joins)

weapons:
id | name
----------
1  | sword
2  | shield

weapon_tag_links:
tag_id  | weapon_id
-------------------
62      | 1
80      | 1
80      | 2
60      | 2

weapon_tags:
tag_id | tag
--------------
60      | red
62      | blue
80      | old

Search query:

SELECT DISTINCT * FROM weapons as w
LEFT JOIN weapon_tag_links AS l ON l.weapon_id = w.id 
INNER JOIN weapon_tags AS t ON l.tag_id = t.tag_id 
WHERE EXISTS (
    ****** Something to go here *******
    WHERE t.tag = ‘blue’
) AND EXISTS (
    ****** Something to go here *******
    WHERE t.tag = ‘old’
)

I’m just missing the link you need in EXISTS. But I’m not sure how to add this…

The Question:
Say I want to search for a record in weapons that is blue AND old, (a sword in this case) how do I do this?

I'm not saying I MUST use "EXISTS" but I want the best optimised way to search for posts with certain tags connected by AND!


回答1:


Try this:

SELECT * FROM wp_posts AS p
LEFT JOIN wp_term_relationships AS tr ON p.ID = tr.object_id 
LEFT JOIN wp_terms AS t ON tr.term_taxonomy_id = t.term_id 
WHERE p.id IN 
(
    SELECT p2.id FROM wp_posts AS p2
    LEFT JOIN wp_term_relationships AS tr2 ON p2.ID = tr2.object_id 
    LEFT JOIN wp_terms AS t2 ON tr2.term_taxonomy_id = t2.term_id 
    GROUP BY p2.id
    HAVING FIND_IN_SET('blue', GROUP_CONCAT(t2.term)) AND FIND_IN_SET('old', GROUP_CONCAT(t2.term))
)



回答2:


Turn it around. That is start with tag IN ('blue', 'old') in the innermost subquery. From that, find the tag_ids. From them, find the weapon_ids HAVING COUNT(*) = 2. Last, in the outermost query, get the weapon.name.

No EXISTS(). No LEFT JOINs; I'm not sure you needed LEFT in the first place.

Your original attempt needed to join to tag twice with different aliases. I avoid that by using the IN and HAVING.

More

Yeah, that gets a bit tricky...

SELECT
     FROM  
      ( SELECT  weapon_id
            FROM  weapon_tags
            JOIN  weapon_tag_links USING(tag_id)
            WHERE  tag IN ('blue', 'old')
            GROUP BY  weapon_id
            HAVING  COUNT(*) >= 2 
      ) a
    JOIN  weapons w ON w.id = a.weapon_id;

It would be easier if you had not normalized tags.



来源:https://stackoverflow.com/questions/36246152/finding-posts-with-tag1-and-tag2-using-a-join-table-exists-having-subquer

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!