如何将含NOT IN的PostgreSQL查询转换为Diesel查询?
在Diesel中实现"NOT IN"子查询获取用户未达成的成就
我有achievements和user_achievements两张表,结构如下:
achievements -------------- | id | name | | 1 | one | | 2 | two | | 3 | three | -------------- user_achievements --------------------------------- | id | user_id | achievement_id | | 1 | 1 | 1 | | 2 | 1 | 2 | | 3 | 2 | 3 | ---------------------------------
用PostgreSQL写的目标查询(获取指定用户未达成的成就):
SELECT a.* FROM achievements as a WHERE a.id NOT IN (SELECT ua.achievement_id FROM user_achievements as ua WHERE user_id = 2);
查询结果:
-------------- | id | name | | 1 | one | | 2 | two | --------------
尝试转成Diesel代码时遇到问题:找不到类似not_eq_any的方法,最初的实现代码:
pub fn get_achievements_not_achieved_by_user(&self, the_user_id: i32) -> QueryResult<Vec<Achievement>> { let conn = &mut self.pool.get().unwrap(); achievements.filter(!id.eq_any(user_achievements.select(achievement_id).filter(user_id.eq(the_author_id)))).get_results(conn) }
解决方法
Diesel没有直接的not_eq_any,但有两种方式实现需求:
方式一:使用ne_all方法(推荐,语义更清晰)
ne_all对应SQL里的NOT IN,直接用它来筛选不在子查询结果里的记录,同时要修正你代码里的变量名错误(the_author_id应该是参数the_user_id):
pub fn get_achievements_not_achieved_by_user(&self, the_user_id: i32) -> QueryResult<Vec<Achievement>> { let conn = &mut self.pool.get().unwrap(); // 先获取该用户已解锁的成就ID集合 let achieved_ids = user_achievements .select(achievement_id) .filter(user_id.eq(the_user_id)); // 筛选出未解锁的成就 achievements .filter(id.ne_all(achieved_ids)) .get_results(conn) }
方式二:对eq_any取反
和你最初的思路一致,只要修正变量名错误即可:
pub fn get_achievements_not_achieved_by_user(&self, the_user_id: i32) -> QueryResult<Vec<Achievement>> { let conn = &mut self.pool.get().unwrap(); achievements .filter(!id.eq_any(user_achievements.select(achievement_id).filter(user_id.eq(the_user_id)))) .get_results(conn) }
注意点
- 你原来的代码里把参数
the_user_id误写为the_author_id,这会导致编译错误或者逻辑错误,必须修正。 ne_all是Diesel专门为NOT IN场景提供的API,可读性比取反eq_any更好,更推荐使用。
内容的提问来源于stack exchange,提问作者Manuelarte
相关产品推荐
相关产品推荐

