简体   繁体   中英

Compare Results of MySQL Query against array

I'm using the following query to join an intermediate table with two others, to generate a list of threads which have tags associated with them.

SELECT th.thread_id, owner_id, message, TIME
    FROM threads th
        INNER JOIN tag_thread_xref ttx 
            ON th.thread_id = ttx.thread_id
        INNER JOIN tags t 
            ON ttx.tag_id = t.tag_id                                    
    ORDER BY TIME DESC                              
    LIMIT ?         

I can then use a WHERE clause to find threads which are associated with a particular tag I'm searching for. My problem though figuring out how to get a list of all rows in table 'threads' which are associated with every element in an array of tag_names. For example, if the user does a search for tags 'television' and 'how-i-met-your-mother', I want to return only threads with contain BOTH of those tags.

I'm inexperienced with MySQL, but it seems like I want to create a fourth temporary table which contains only one column; my list of tag_name's. I then want to add that table as a third INNER JOIN to my query to constrain the list. I've done that here:

CREATE TEMPORARY TABLE tmp_tag_list (tag_name VARCHAR(30));
INSERT INTO tmp_tag_list (tag_name) VALUES ('television');
INSERT INTO tmp_tag_list (tag_name) VALUES ('how-i-met-your-mother');

SELECT th.thread_id,owner_id,message,time
    FROM threads th                                             
        INNER JOIN tag_thread_xref ttx                                                  
            ON th.thread_id = ttx.thread_id                                     
        INNER JOIN tags t                                                   
            ON ttx.tag_id = t.tag_id
        INNER JOIN tmp_tag_list tmp
            ON t.tag_name = tmp.tag_name                                                                                    
    ORDER BY TIME DESC                                              
    LIMIT 10

I expect the left table to be a collection of all rows from 'threads' which have a 'tag_id' associated with them; all tagged threads, and the right table to be a list of the tags I want to filter by. So I would expect the resulting inner join table to be only threads which have the tags listed in the right table.

I have two problems:

  1. This isn't the result I'm getting. Instead, this query returns all threads which have ANY of the tags listed, and some rows appear twice.

  2. Even if I do get this to work as expected, I don't see how I can use PHP's prepared statements to dynamically create as many INSERT clauses as are required for my array; I would have to revert back to PHP's old, less secure method of MySQL queries, unless there is another way.

Can anyone offer input on either of these issues?

There is probably a better way to do this, but off the top of my head, you could select where it is either tag, group by thread_id and tag, and then select from that, group by thread_id, where the count of tags (which you grouped by, so will only have one result for each tag) is at least two. Something like:

SELECT th.thread_id, owner_id, message, time FROM 
   (SELECT th.thread_id,owner_id,message,time,tag_name
    FROM threads th                                             
    INNER JOIN tag_thread_xref ttx                                                  
    ON th.thread_id = ttx.thread_id                                     
    INNER JOIN tags t                                                   
    ON ttx.tag_id = t.tag_id
    WHERE tag_name IN (<?php echo implode(",",$sanitizedTagsArray); ?>)
    GROUP BY th.thread_id, tag_name) AS t
GROUP BY th,thread_id
HAVING count(tag_name) >= <?php echo count($sanitizedTagsArray); ?>

If you want to use PDO:

$questionmarks = str_repeat("?,", count($tags)-1) . "?";    
...
WHERE tag_name IN (<?php echo $questionmarks ?>)
...
HAVING count(tag_name) >= <?php echo count($tags); ?>
...
$stmt = $db->prepare($query);
$stmt->execute($tags);

And then you're totally safe

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM