SELECT tp.id as projectId,tp.project_name as projectName,tp.project_type as projectType, tipp.project_status as projectStatus FROM tzgl_project tp left join tzgl_investment_plan_project tipp on tipp.project_id = tp.id left join tzgl_investment_plan tip on tipp.plan_id = tip.id and tip.plan_year = 2023 and tip.plan_type in (1,3) where tp.id = '429158807596360069' and tip.is_deleted = 0 优化sql语句,实现当tipp.plan_type=1和tipp.plan_type=3都有值的时候,只查询到tipp.plan_type=3的一条值
时间: 2024-03-11 14:48:16 浏览: 153
可以使用子查询的方式来实现:
```
SELECT tp.id as projectId, tp.project_name as projectName, tp.project_type as projectType,
(SELECT project_status FROM tzgl_investment_plan_project WHERE project_id = tp.id AND plan_id = tip.id AND plan_type = 3 limit 1) as projectStatus
FROM tzgl_project tp
LEFT JOIN tzgl_investment_plan_project tipp ON tipp.project_id = tp.id
LEFT JOIN tzgl_investment_plan tip ON tipp.plan_id = tip.id AND tip.plan_year = 2023 AND tip.plan_type IN (1,3)
WHERE tp.id = '429158807596360069' AND tip.is_deleted = 0
```
这样可以避免查询出多条符合条件的记录,只查询出符合条件的一条。
相关问题
<select id="queryCurrentDateAttendance" resultType="com.zj.project.modular.project.vo.CurrentDateAttendanceVO"> SELECT a.companyId, a.companyName, a.employees, COALESCE(c.workers, 0) AS workers FROM ( SELECT pp.company_id AS companyId, pc.company_name AS companyName, COUNT(pp.id_card_no) AS employees FROM project_personnel pp LEFT JOIN project_company pc ON pp.company_id = pc.id WHERE pp.project_id = #{dto.projectId} AND pp.cur_state = 'IN' AND pp.create_time < #{dto.endTime} GROUP BY pp.company_id ) AS a LEFT JOIN ( SELECT companyId, SUM(workers) AS workers FROM( SELECT pp.company_id AS companyId, COUNT(DISTINCT DATE(tpar.create_time)) AS workers FROM project_personnel pp LEFT JOIN tj_personnel_attendance_record tpar ON pp.id_card_no = tpar.id_card_no AND pp.post_name = tpar.post_name AND pp.company_id = tpar.company_id WHERE tpar.project_id = #{dto.projectId} AND pp.cur_state = 'IN' AND tpar.create_time BETWEEN #{dto.startTime} AND #{dto.endTime} GROUP BY pp.company_id,tpar.id_card_no) AS b GROUP BY companyId ) AS c ON a.companyId = c.companyId </select> AND pp.create_time < #{dto.endTime}这里报红
<think>好的,我现在需要帮助用户解决SQL语句中“pp.create_time < #{dto.endTime}”报红的问题。首先,我得仔细分析用户提供的代码和问题描述。
用户提供的SQL片段是一个MyBatis的映射文件中的查询语句。报红的部分在子查询a的WHERE条件里:`pp.create_time < #{dto.endTime}`。用户提到这里报红,可能是指IDE或XML解析器提示错误,或者MyBatis运行时出错。
首先,我应该检查XML结构是否正确。用户提供的SQL中有两个子查询a和c,结构看起来是嵌套正确的,标签闭合也没有问题。可能的问题点在于参数的使用或者SQL语法。
接下来,查看参数占位符是否正确。MyBatis中使用`#{}`来引用参数,这里用的是`#{dto.projectId}`和`#{dto.endTime}`,假设dto是传入的参数对象,属性名正确的话,这部分应该没问题。但需要注意参数类型是否匹配,比如endTime是否是日期类型,而create_time在数据库中是日期或时间戳类型。
然后,检查SQL语句本身是否有语法错误。在子查询a中,WHERE子句的条件是:
`pp.project_id = #{dto.projectId}`
`AND pp.cur_state = 'IN'`
`AND pp.create_time < #{dto.endTime}`
这里可能存在两个问题:一是表别名是否正确,比如pp是否正确定义。在FROM子句中,project_personnel的别名是pp,所以pp.create_time是存在的字段。另一个可能是比较运算符和参数类型是否匹配,比如endTime是否是字符串形式,需要正确的日期格式。
另一个可能性是XML中的特殊字符未转义。例如,`<`符号在XML中需要转义为`<`。用户代码中的`<`直接使用可能导致XML解析错误,从而报红。这是常见的问题,尤其是在MyBatis的XML映射文件中,条件中的比较运算符如果包含`<`或`>`,必须进行转义,否则会导致XML解析失败。
所以,用户的问题很可能是因为`<`符号未转义。正确的做法是将`<`替换为`<`,或者在XML的CDATA块中包裹SQL语句,避免特殊字符被解析。例如:
```xml
<![CDATA[
pp.create_time < #{dto.endTime}
]]>
```
但用户当前的SQL并没有使用CDATA,所以直接转义符号更合适。修改后的条件应该是:
`AND pp.create_time < #{dto.endTime}`
此外,还需要检查是否有其他地方的`<`或`>`未转义,例如在子查询c中的BETWEEN部分,但那里的`<`可能没有问题,因为BETWEEN后面的时间比较可能使用了正确的占位符,且参数传递正确。
总结可能的原因:
1. XML中的`<`未转义,导致解析错误。
2. 参数类型不匹配,例如endTime不是日期类型,或者格式不正确。
3. 表别名或字段名错误,pp.create_time不存在。
根据用户提供的信息,最可能的原因是第一个,即XML特殊字符未转义。因此,解决方案是将`<`替换为`<`。</think>这个SQL语句中出现`pp.create_time < #{dto.endTime}`报红的原因可能有以下几点,我分步骤为您分析:
$$
\begin{aligned}
1. \ &\textcolor{blue}{XML特殊字符问题:} \\
&\quad \text{在XML中,} \textcolor{red}{<} \text{符号必须转义为} \textcolor{green}{<} \\
&\quad \text{原句应改为} \textcolor{green}{pp.create_time < \#{dto.endTime}} \\
\\
2. \ &\textcolor{blue}{时间格式问题:} \\
&\quad \text{确保} \textcolor{red}{\#{dto.endTime}} \text{传入的是合法时间格式} \\
&\quad \text{推荐使用} \textcolor{green}{yyyy-MM-dd HH:mm:ss} \\
\\
3. \ &\textcolor{blue}{字段可见性问题:} \\
&\quad \text{确认} \textcolor{red}{project\_personnel} \text{表确实存在} \textcolor{green}{create\_time} \text{字段} \\
&\quad \text{且表别名} \textcolor{green}{pp} \text{正确定义}
\end{aligned}
$$
建议按照优先级依次排查:
1. **首先处理XML转义问题**(最常见原因):
```xml
WHERE pp.project_id = #{dto.projectId}
AND pp.cur_state = 'IN'
AND pp.create_time < #{dto.endTime} <!-- 修改此处 -->
```
2. **验证时间参数格式**(使用日志或调试工具):
```java
// 伪代码示例:打印查看参数值
System.out.println("endTime格式:" + dto.getEndTime());
// 应该输出类似 2024-01-01 23:59:59
```
3. **检查字段定义**(数据库层面验证):
```sql
-- 执行desc查看表结构
DESC project_personnel;
-- 确认包含create_time字段
```
**扩展建议**:对于复杂的SQL语句,推荐使用`<![CDATA[ ]]>`包裹:
```xml
WHERE pp.project_id = #{dto.projectId}
<![CDATA[
AND pp.create_time < #{dto.endTime}
]]>
```
这样可以避免所有XML特殊字符问题,但要注意在CDATA块内部仍然需要保证SQL语法正确。
SELECT t1.review_id AS reviewId, t1.checked_member AS checkedMember, t1.check_member AS checkMember, t1.start_date AS startDate, t_internal.dict_value AS internal, t_status.dict_value AS status, t_phase.dict_value AS checkPhase, t2.project_name AS projectName FROM A01_REVIEW_HEADER t1 INNER JOIN A01_PROJECT t2 ON t1.project_id = t2.project_id INNER JOIN A01_DICTIONARY t_internal ON t1.internal_flag = t_internal.dict_code INNER JOIN A01_DICTIONARY t_status ON t1.status = t_status.dict_code INNER JOIN A01_DICTIONARY t_phase ON t1.check_phase = t_phase.dict_code mapper.xml应该怎么写
<think>我们有一个多表连接查询,需要将SQL语句写入MyBatis的mapper.xml文件。原SQL如下:
```sql
SELECT
t1.review_id AS reviewId,
t1.checked_member AS checkedMember,
t1.check_member AS checkMember,
t1.start_date AS startDate,
t_internal.dict_value AS internal,
t_status.dict_value AS status,
t_phase.dict_value AS checkPhase,
t2.project_name AS projectName
FROM
A01_REVIEW_HEADER t1
INNER JOIN
A01_PROJECT t2
ON t1.project_id = t2.project_id
INNER JOIN
A01_DICTIONARY t_internal
ON t1.internal_flag = t_internal.dict_code
INNER JOIN
A01_DICTIONARY t_status
ON t1.status = t_status.dict_code
INNER JOIN
A01_DICTIONARY t_phase
ON t1.check_phase = t_phase.dict_code
```
在mapper.xml中,我们需要完成以下步骤:
1. 定义resultMap,将查询结果映射到Java对象。
2. 编写select语句,包括多表连接。
3. 注意字段别名与resultMap中的映射关系。
假设我们有一个Java类(例如ReviewResult)来接收查询结果,其属性与查询结果的别名对应。
步骤:
1. 创建Java结果对象(如果尚未存在):
```java
public class ReviewResult {
private String reviewId;
private String checkedMember;
private String checkMember;
private Date startDate;
private String internal; // 来自t_internal.dict_value
private String status; // 来自t_status.dict_value
private String checkPhase; // 来自t_phase.dict_value
private String projectName;
// 省略getter和setter
}
```
2. 在mapper.xml中,我们定义resultMap(可选,如果使用别名自动映射则不需要显式定义resultMap,但这里为了清晰,我们使用resultMap)或者使用resultType。
由于查询中已经使用了别名,并且别名与Java对象的属性名一致(驼峰命名),我们可以使用MyBatis的自动映射(auto-mapping)功能。因此,我们可以直接使用resultType。
但是,如果我们需要更精确的控制(例如类型处理等),或者存在嵌套对象,则使用resultMap。由于这个查询结果都是简单类型,我们可以使用resultType。
因此,我们可以这样写:
```xml
<select id="selectReviewList" resultType="com.example.vo.ReviewResult">
SELECT
t1.review_id AS reviewId,
t1.checked_member AS checkedMember,
t1.check_member AS checkMember,
t1.start_date AS startDate,
t_internal.dict_value AS internal,
t_status.dict_value AS status,
t_phase.dict_value AS checkPhase,
t2.project_name AS projectName
FROM
A01_REVIEW_HEADER t1
INNER JOIN
A01_PROJECT t2
ON t1.project_id = t2.project_id
INNER JOIN
A01_DICTIONARY t_internal
ON t1.internal_flag = t_internal.dict_code
INNER JOIN
A01_DICTIONARY t_status
ON t1.status = t_status.dict_code
INNER JOIN
A01_DICTIONARY t_phase
ON t1.check_phase = t_phase.dict_code
</select>
```
注意:这里假设字典表连接时没有重复的字段,并且我们只需要字典表中的一个字段(dict_value)。另外,我们使用内连接(INNER JOIN),这意味着如果任何一个连接条件不满足,该记录就不会被返回。
但是,如果字典表可能有多个类型(比如同一个dict_code可能属于不同的dict_type),那么我们需要在连接条件中加入类型限制。原SQL中没有,但实际中可能需要。例如:
```sql
INNER JOIN A01_DICTIONARY t_internal
ON t1.internal_flag = t_internal.dict_code
AND t_internal.dict_type = 'INTERNAL_FLAG'
```
根据实际需求调整。
3. 在Mapper接口中定义方法:
```java
public interface ReviewMapper {
List<ReviewResult> selectReviewList();
}
```
如果查询需要参数,可以在接口方法中添加参数,并在XML中使用#{param}来引用。
4. 关于动态SQL:如果查询条件需要根据参数动态变化,可以在select语句中使用MyBatis的动态SQL标签(如<where>, <if>等)。例如,如果我们要根据项目名称进行过滤:
```xml
<select id="selectReviewList" resultType="com.example.vo.ReviewResult">
SELECT ...
<where>
<if test="projectName != null and projectName != ''">
t2.project_name LIKE CONCAT('%', #{projectName}, '%')
</if>
</where>
</select>
```
然后在接口方法中增加参数:
```java
List<ReviewResult> selectReviewList(@Param("projectName") String projectName);
```
总结:
- 使用resultType指向我们定义的结果对象,利用自动映射将查询结果别名映射到对象属性。
- 如果属性名和别名一致(驼峰规则),自动映射可以完成工作。
- 如果存在复杂的映射(如一对多,多对一),则需要使用resultMap进行显式映射。
对于当前查询,由于都是简单属性,使用resultType即可。
注意:如果数据库字段命名和Java属性命名风格不一致(比如数据库是下划线,Java是驼峰),需要在MyBatis配置文件中开启驼峰命名转换:
```xml
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
```
这样,即使查询结果中不使用别名(即直接使用数据库字段名),也能自动映射到驼峰命名的属性。但是,我们在查询中已经使用了别名,所以这个设置不是必须的,但可以作为一个全局配置来简化其他没有使用别名的查询。
因此,在mapper.xml中,我们按照上面的方式编写即可。</think>### MyBatis Mapper.xml 多表连接查询实现方案
以下是针对您提供的 SQL 查询的完整 MyBatis mapper.xml 实现方案,包含多表连接和字段映射:
```xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.ReviewMapper">
<!-- 结果映射配置 -->
<resultMap id="reviewResultMap" type="com.example.vo.ReviewVO">
<id property="reviewId" column="reviewId" />
<result property="checkedMember" column="checkedMember"/>
<result property="checkMember" column="checkMember"/>
<result property="startDate" column="startDate"/>
<result property="internal" column="internal"/>
<result property="status" column="status"/>
<result property="checkPhase" column="checkPhase"/>
<result property="projectName" column="projectName"/>
</resultMap>
<!-- 多表连接查询SQL -->
<select id="selectReviewDetails" resultMap="reviewResultMap">
SELECT
t1.review_id AS reviewId,
t1.checked_member AS checkedMember,
t1.check_member AS checkMember,
t1.start_date AS startDate,
t_internal.dict_value AS internal,
t_status.dict_value AS status,
t_phase.dict_value AS checkPhase,
t2.project_name AS projectName
FROM
A01_REVIEW_HEADER t1
INNER JOIN A01_PROJECT t2
ON t1.project_id = t2.project_id
INNER JOIN A01_DICTIONARY t_internal
ON t1.internal_flag = t_internal.dict_code
AND t_internal.dict_type = 'INTERNAL_FLAG' <!-- 添加字典类型过滤 -->
INNER JOIN A01_DICTIONARY t_status
ON t1.status = t_status.dict_code
AND t_status.dict_type = 'REVIEW_STATUS' <!-- 添加字典类型过滤 -->
INNER JOIN A01_DICTIONARY t_phase
ON t1.check_phase = t_phase.dict_code
AND t_phase.dict_type = 'CHECK_PHASE' <!-- 添加字典类型过滤 -->
<!-- 动态条件 -->
<where>
<if test="projectId != null">
AND t1.project_id = #{projectId}
</if>
<if test="status != null">
AND t1.status = #{status}
</if>
<if test="startDateFrom != null">
AND t1.start_date >= #{startDateFrom}
</if>
<if test="startDateTo != null">
AND t1.start_date <= #{startDateTo}
</if>
</where>
ORDER BY t1.start_date DESC
</select>
</mapper>
```
#### 关键实现说明
1. **结果映射(resultMap)**:
- 使用 `<resultMap>` 定义查询结果与Java对象的映射关系
- `<id>` 标签标识主键字段
- `<result>` 标签映射普通字段,其中:
- `property` 对应Java对象属性名
- `column` 对应SQL查询中的别名
- 解决数据库下划线命名到Java驼峰命名的自动转换[^1]
2. **多表连接处理**:
```xml
INNER JOIN A01_DICTIONARY t_internal
ON t1.internal_flag = t_internal.dict_code
AND t_internal.dict_type = 'INTERNAL_FLAG'
```
- 添加 `dict_type` 条件确保字典项类型匹配
- 使用 `INNER JOIN` 确保必须存在关联记录
3. **动态SQL条件**:
```xml
<where>
<if test="projectId != null">
AND t1.project_id = #{projectId}
</if>
<if test="status != null">
AND t1.status = #{status}
</if>
</where>
```
- `<where>` 标签自动处理条件前缀
- `<if>` 标签实现条件过滤[^1]
4. **Mapper接口定义**:
```java
public interface ReviewMapper {
List<ReviewVO> selectReviewDetails(
@Param("projectId") Long projectId,
@Param("status") String status,
@Param("startDateFrom") Date startDateFrom,
@Param("startDateTo") Date startDateTo
);
}
```
#### 优化建议
1. **结果集映射优化**:
```xml
<!-- 启用自动驼峰命名转换 -->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
```
在MyBatis配置文件中启用此设置可减少手动映射工作量[^1]
2. **字典表连接复用**:
```xml
<!-- 定义可复用的字典连接片段 -->
<sql id="dictionaryJoin">
INNER JOIN A01_DICTIONARY ${alias}
ON ${column} = ${alias}.dict_code
AND ${alias}.dict_type = #{type}
</sql>
<!-- 使用片段 -->
<include refid="dictionaryJoin">
<property name="alias" value="t_internal"/>
<property name="column" value="t1.internal_flag"/>
<property name="type" value="INTERNAL_FLAG"/>
</include>
```
3. **分页支持**:
```xml
<!-- 添加分页参数 -->
<select id="selectReviewDetails" resultMap="reviewResultMap">
SELECT ...
LIMIT #{pageSize} OFFSET #{offset}
</select>
```
#### 常见问题解决
1. **列名冲突问题**:
- 解决方案:为每个表的字段指定唯一别名
```sql
SELECT
t1.review_id AS t1_reviewId,
t2.project_name AS t2_projectName
```
2. **字典表类型过滤**:
- 关键:添加 `dict_type` 条件避免错误匹配
- 错误示例:缺少类型过滤导致错误字典值[^2]
3. **性能优化**:
- 为连接字段添加索引:`t1.project_id`, `t_internal.dict_code`
- 避免 `SELECT *`,明确指定所需字段
此实现方案完整保留了原始SQL的多表连接逻辑,同时增加了类型安全的字典表过滤和动态查询条件支持,符合MyBatis最佳实践[^2]。
阅读全文
相关推荐

















