LambdaQueryWrapper 中如何编写not exists子查询
时间: 2025-06-09 22:54:12 浏览: 14
<think>好的,用户现在问的是如何在LambdaQueryWrapper中编写not exists子查询。之前用户的问题是关于SQL查询中如何找出所有检验项目都为ok的检验单,我给出了两种SQL方法,其中一种是使用NOT EXISTS子查询。现在用户可能是在实际项目中使用了MyBatis-Plus,所以需要将这个SQL逻辑转换成LambdaQueryWrapper的写法。
首先,我需要回忆一下MyBatis-Plus的LambdaQueryWrapper的特性。LambdaQueryWrapper提供了lambda表达式的方式来构建查询条件,避免直接写字符串,增加类型安全性。对于子查询,通常可以使用inSql或exists方法,但exists方法可能更适用于存在性检查。
用户之前的问题中的SQL示例是使用NOT EXISTS来排除有ng结果的检验单。对应的SQL是:
SELECT DISTINCT TestID FROM test_details AS t1 WHERE NOT EXISTS (SELECT 1 FROM test_details AS t2 WHERE t2.TestID = t1.TestID AND t2.Result = 'ng');
需要将这个转换成LambdaQueryWrapper的写法。这里可能需要使用到子查询的构建,即创建一个子查询,检查是否存在同一TestID下有ng的情况,然后外层查询排除这些TestID。
在MyBatis-Plus中,可以使用QueryWrapper的子查询,通过lambda表达式引用主查询的字段。例如,在notExists方法中,构建一个子查询,条件为TestID等于主查询的TestID,并且Result为'ng'。然后外层查询选择那些不存在这种情况的TestID。
具体来说,可能需要这样构建:
LambdaQueryWrapper<TestDetails> wrapper = new LambdaQueryWrapper<>();
wrapper.notExists(subQuery -> subQuery
.select(TestDetails::getTestId)
.eq(TestDetails::getResult, "ng")
.eq(TestDetails::getTestId, TestDetails::getTestId) // 这里可能需要主查询的别名,但Lambda可能无法直接引用,可能需要使用别名或参数?
);
不过这里可能存在一个问题,即如何让子查询中的TestId等于外层查询的TestId。在Lambda表达式中,可能需要使用别名或者特定的方法来实现关联子查询。可能需要使用apply方法或者在子查询中使用参数占位符,手动关联外层和子查询的字段。
另外,用户可能需要的是查询所有TestID,所以可能需要使用selectDistinct(TestDetails::getTestId)来确保结果唯一。
可能需要查阅MyBatis-Plus的文档,确认如何正确关联外层和子查询的字段。比如,在子查询中,使用.eq(TestDetails::getTestId, TestDetails::getTestId),但这里可能需要外层的别名,或者使用一个函数式的方法来引用外层字段。
或者,可能需要使用SqlUtils的语法,例如在子查询中使用"{0}"来引用外层表的字段,然后通过参数设置。例如:
wrapper.notExists(sub -> sub
.select("1")
.eq("test_id", "t1.test_id")
.eq("result", "ng")
);
但这里需要确保外层查询的表别名是t1,或者在LambdaQueryWrapper中设置别名。可能Lambda方式下设置别名不太方便,可能需要使用字符串的方式。
这可能会导致在LambdaQueryWrapper中混合使用lambda和字符串的情况,可能不够类型安全,但可能是必要的。
总结来说,用户的需求是将之前的NOT EXISTS子查询转换成LambdaQueryWrapper的写法。可能需要使用notExists方法,构建子查询,关联外层和内层的TestID,并检查是否存在ng的结果。同时,外层查询需要选择那些不存在的TestID,可能需要使用distinct来去重。
需要给出具体的示例代码,并解释每一步的作用,以及可能的注意事项,比如字段名是否正确,是否需要别名,性能方面的考虑,比如索引等。此外,可能需要提醒用户根据实际实体类名和字段名进行调整,并测试生成的SQL是否正确。</think>在 MyBatis-Plus 中使用 `LambdaQueryWrapper` 实现 `NOT EXISTS` 子查询,可通过以下方式实现:
---
### **1. 场景还原**
假设存在 **检验项目表** `test_item`:
```java
@Data
public class TestItem {
private Long id; // 主键
private String testId; // 检验单号
private String item; // 检验项目
private String result; // 结果("ok"或"ng")
}
```
---
### **2. NOT EXISTS 实现逻辑**
需求:**查询所有检验项目均为 `ok` 的检验单号 (testId)**
等价于 SQL:
```sql
SELECT DISTINCT test_id
FROM test_item t1
WHERE NOT EXISTS (
SELECT 1
FROM test_item t2
WHERE t2.test_id = t1.test_id
AND t2.result = 'ng'
);
```
---
### **3. LambdaQueryWrapper 实现**
```java
LambdaQueryWrapper<TestItem> wrapper = new LambdaQueryWrapper<>();
wrapper.select(TestItem::getTestId) // 选择需要查询的字段
.groupBy(TestItem::getTestId) // 按检验单号分组
.notExists( // NOT EXISTS 子查询
"SELECT 1 FROM test_item t2 WHERE t2.test_id = test_item.test_id AND t2.result = 'ng'"
);
List<TestItem> result = testItemMapper.selectList(wrapper);
```
---
### **4. 代码解释**
| 方法 | 作用 |
|--------------------|--------------------------------------------------------------------|
| `.select()` | 指定查询字段(避免查询全部字段) |
| `.groupBy()` | 去重检验单号(等价于 SQL 的 `DISTINCT`) |
| `.notExists()` | 注入子查询语句,关联主表 `test_item` 和子查询 `t2` 的 `test_id`,过滤存在 `ng` 的记录 |
---
### **5. 动态条件扩展**
若需要动态条件(如时间范围),可使用 **参数占位符**:
```java
wrapper.notExists(
"SELECT 1 FROM test_item t2 WHERE t2.test_id = test_item.test_id " +
"AND t2.result = 'ng' " +
"AND t2.create_time > {0}",
LocalDateTime.now().minusDays(7) // 动态参数
);
```
---
### **6. 性能优化建议**
1. **索引**:在 `test_id` 和 `result` 字段建立联合索引
2. **缓存**:高频查询结果可缓存
3. **分页**:大数据量时添加 `.last("LIMIT 1000")` 限制结果集
---
通过这种方式,可以在保持 MyBatis-Plus 的 Lambda 表达式风格的同时,实现复杂的子查询逻辑。
阅读全文
相关推荐







