PHP PDO查询去重:博客搜索功能重复结果问题
解决博客搜索功能的重复结果问题
这个问题我之前也碰到过,核心原因是你循环执行查询的方式导致的——每一个搜索关键词都会触发一次查询,只要文章匹配当前关键词就会被返回,同一个文章如果匹配多个关键词(比如同时包含Smith和Will),就会在每次循环里都被输出一遍,自然就重复了。
下面给你两种靠谱的解决思路:
思路一:合并查询条件,单次查询获取结果
最高效的方式是把所有搜索关键词的匹配逻辑合并到一条SQL里,不用循环执行多次查询。这样数据库只会返回一次符合任意关键词的结果,天然避免重复。
修改后的代码示例:
else { $searchbit = explode(" ", $searchterm); // 先处理每个关键词的metaphone值,同时构建SQL的条件部分 $conditions = []; $params = []; foreach($searchbit as $index => $value){ $metaphonebit = metaphone($value); $paramKey = ":metaphonebit_$index"; // 每个关键词匹配三个字段的任意一个 $conditions[] = "(metatext.metatitle LIKE $paramKey OR metatext.metatags LIKE $paramKey OR metatext.metacategory LIKE $paramKey)"; $params[$paramKey] = "%$metaphonebit%"; } // 把所有条件用OR连接起来 $conditionStr = implode(" OR ", $conditions); $sql = "SELECT DISTINCT blogcontent.title, blogcontent.content, blogcontent.blogtimestamp, blogcontent.views, blogcontent.image, blogcontent.author, blogcontent.tdate, blogcontent.category, blogcontent.tags, categories.banner, blogcontent.id, categories.color, metatext.metatitle, categories.name FROM blogcontent JOIN categories ON blogcontent.category = categories.name JOIN metatext ON metatext.metaid = blogcontent.id WHERE $conditionStr"; $query = $con->prepare($sql); $query->execute($params); $results = $query->fetchAll(PDO::FETCH_OBJ); if($query->rowCount() > 0) { foreach($results as $result){ echo htmlentities($result->title); echo htmlentities($result->category); } } }
这里做了几个关键优化:
- 用
DISTINCT确保数据库返回的结果没有重复(即使同一个文章匹配多个关键词,也只会返回一次) - 循环构建条件和参数,避免SQL注入,同时把所有关键词的匹配逻辑合并成一个
OR连接的条件 - 单次查询减少数据库交互,性能更优
思路二:循环查询后去重
如果因为某些原因必须保留循环查询的方式,可以把每次查询的结果先存入数组,用文章ID作为唯一标识去重,最后再统一输出。
修改后的代码示例:
else { $searchbit = explode(" ", $searchterm); $uniqueResults = []; // 用id作为键存储唯一结果 foreach($searchbit as $value){ $metaphonebit = metaphone($value); $sql = "SELECT blogcontent.title, blogcontent.content, blogcontent.blogtimestamp, blogcontent.views, blogcontent.image, blogcontent.author, blogcontent.tdate, blogcontent.category, blogcontent.tags, categories.banner, blogcontent.id, categories.color, metatext.metatitle, categories.name FROM blogcontent JOIN categories ON blogcontent.category = categories.name JOIN metatext ON metatext.metaid = blogcontent.id WHERE (metatext.metatitle LIKE :metaphonebit OR metatext.metatags LIKE :metaphonebit OR metatext.metacategory LIKE :metaphonebit )"; $query = $con->prepare($sql); $query->bindValue(':metaphonebit', '%' . $metaphonebit . '%'); $query->execute(); $results = $query->fetchAll(PDO::FETCH_OBJ); if($query->rowCount() > 0) { foreach($results as $result){ // 用文章id作为键,确保同一个id只存一次 $uniqueResults[$result->id] = $result; } } } // 遍历去重后的结果输出 foreach($uniqueResults as $result){ echo htmlentities($result->title); echo htmlentities($result->category); } }
这个思路的核心是用数组的键唯一性来过滤重复的文章,把每个查询到的结果以id为键存入数组,后续相同id的结果会覆盖之前的,最后输出的时候就是唯一的结果。
推荐优先用第一种思路,因为单次查询的性能比多次循环查询要好很多,尤其是搜索关键词多的时候差异更明显。
内容的提问来源于stack exchange,提问作者Kudzie99




