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:
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.
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.