<if test="passDownTime != null"> <choose> <when test="params.exportDays == null"> <!-- 直接使用日期对象,避免TO_DATE转换 --> AND pass_down_time = #{passDownTime, jdbcType=TIMESTAMP} </when> <otherwise> <if test="params.exportDays > 0"> <!-- 使用Oracle原生日期计算 --> AND pass_down_time BETWEEN #{passDownTime, jdbcType=TIMESTAMP} - (#{params.exportDays} - 1) AND #{passDownTime, jdbcType=TIMESTAMP} </if> </otherwise> </choose> </if> 这段我想改成截取passDownTime,截取到2025-07-20也就是获取当天的数据
时间: 2025-07-20 11:15:13 浏览: 10
<think>我们想要修改SQL,使得passDownTime被截取到当天日期(即去掉时分秒部分),然后获取当天的数据。
在Oracle中,我们可以使用TRUNC函数来截取日期到天。例如,TRUNC(pass_down_time) 会返回pass_down_time的日期部分(时分秒被置为00:00:00)。
原SQL中的条件部分:
pass_down_time between to_date(SUBSTR(?,0,18),'yyyy-mm-dd hh24:mi:ss') - (? - 1) and to_date(SUBSTR(?,0,18),'yyyy-mm-dd hh24:mi:ss')
这个条件原本是为了根据传入的时间参数和天数参数来查询一段时间内的数据。但是,现在用户的需求是修改为只获取当天的数据(即passDownTime截取到当天)。
因此,我们可以将条件改为:
TRUNC(pass_down_time) = TRUNC(?)
注意:这里我们使用TRUNC函数将pass_down_time截取到天,同时也将传入的参数截取到天,然后比较。
但是,需要注意的是,原SQL中还有shift参数,我们也要保留。
修改后的SQL条件部分:
WHERE TRUNC(pass_down_time) = TRUNC(?) AND shift = ?
另外,原SQL中使用了两个相同的参数(都是passDownTime)和一个整数参数(exportDays)以及shift。现在我们去掉了exportDays,只需要一个日期参数和shift参数。
因此,我们需要调整传入的参数。原调用中传入了两个相同的日期参数和一个整数参数(1)以及shift(0)。现在只需要传入一个日期参数和shift参数。
但是,注意原SQL中还有排序等部分,我们保留。
修改后的完整SQL(去掉原来关于exportDays的条件,替换为当天的条件):
SELECT *
FROM (
SELECT ttcpd.id, ttcpd.pass_down_desc, ttcpd.dept_id, ttcpd.pass_down_time, ttcpd.shift, ttcpd.pi_run_pick, ttcpd.cham, ttcpd.owner, ttcpd.status, ttcpd.comments, ttcpd.capability_id, ttcpd.item_file, ttcc.capability, ttcc.sort, ttcpd.create_type
FROM t_tool_cham_pass_down ttcpd
LEFT JOIN t_tool_cham_capability ttcc ON ttcc.id = ttcpd.capability_id
) t
WHERE TRUNC(pass_down_time) = TRUNC(?)
AND shift = ?
ORDER BY sort ASC, cham ASC, pass_down_time
注意:使用TRUNC函数可能会影响性能,因为如果pass_down_time上有索引,那么使用TRUNC(pass_down_time)会导致索引失效。如果数据量大,可以考虑使用范围查询(例如:pass_down_time >= ? and pass_down_time < ? + 1),这样可以利用索引。
因此,另一种写法(推荐):
WHERE pass_down_time >= TRUNC(?)
AND pass_down_time < TRUNC(?) + 1
AND shift = ?
这样,第一个条件:大于等于当天的0点0分0秒,第二个条件:小于第二天的0点0分0秒(即当天的23:59:59.999...)。
在Oracle中,TRUNC(?)返回的是当天的0点0分0秒,然后加上1表示加1天(即第二天的0点0分0秒),所以小于这个值就表示在当天之内。
这样写的好处是:如果pass_down_time上有索引,则可以使用索引范围扫描,效率更高。
所以,我们采用第二种方式。
修改后的SQL(条件部分):
WHERE pass_down_time >= TRUNC(?)
AND pass_down_time < TRUNC(?) + 1
AND shift = ?
注意:这里需要传入两个相同的日期参数(同一个值)和shift参数。
因此,MyBatis的XML中,我们可以这样写(去掉原来关于exportDays的条件,替换为新的条件):
<if test="passDownTime != null">
AND pass_down_time >= TRUNC(#{passDownTime})
AND pass_down_time < TRUNC(#{passDownTime}) + 1
</if>
同时,保留shift条件(注意原条件中shift是直接等于传入的shift值,所以保留)。
但是,注意原SQL中还有排序,我们保留。
另外,原SQL中使用了两个相同的日期参数(即passDownTime),现在我们也需要两个相同的日期参数(在MyBatis中,同一个参数可以重复使用)。
因此,修改后的SQL语句为:
SELECT * FROM (
SELECT ttcpd.id, ttcpd.pass_down_desc, ttcpd.dept_id, ttcpd.pass_down_time, ttcpd.shift, ttcpd.pi_run_pick, ttcpd.cham, ttcpd.owner, ttcpd.status, ttcpd.comments, ttcpd.capability_id, ttcpd.item_file, ttcc.capability, ttcc.sort, ttcpd.create_type
FROM t_tool_cham_pass_down ttcpd
LEFT JOIN t_tool_cham_capability ttcc ON ttcc.id = ttcpd.capability_id
) t
WHERE pass_down_time >= TRUNC(#{passDownTime})
AND pass_down_time < TRUNC(#{passDownTime}) + 1
AND shift = #{shift}
ORDER BY sort ASC, cham ASC, pass_down_time
注意:在Oracle中,TRUNC函数返回的是DATE类型(不带时分秒),而pass_down_time如果是TIMESTAMP类型,Oracle会自动转换(隐式转换)为DATE类型进行比较。但是,为了确保类型一致,我们也可以将参数转换为DATE类型再比较。不过,如果pass_down_time是TIMESTAMP类型,那么使用TRUNC(?)返回的是DATE类型,比较时会将pass_down_time也隐式转换为DATE类型,这可能导致索引失效(如果索引是TIMESTAMP类型)。因此,更精确的做法是使用CAST或者将参数转换为TIMESTAMP类型。但是,由于TRUNC(?)返回的是当天的0点0分0秒,我们可以将其转换为TIMESTAMP类型,这样就不会改变索引的使用。
然而,在Oracle中,TRUNC函数也可以用于TIMESTAMP类型,返回的是TIMESTAMP类型。所以,如果pass_down_time是TIMESTAMP类型,我们可以使用:
TRUNC(#{passDownTime}) -- 如果参数是DATE类型,则返回DATE类型;如果参数是TIMESTAMP类型,则返回TIMESTAMP类型。
但是,在MyBatis中,我们传入的passDownTime可能是java.util.Date类型,MyBatis会将其转换为Oracle的TIMESTAMP类型(即使用setTimestamp方法)。因此,我们需要确保TRUNC函数能够处理TIMESTAMP类型。
实际上,Oracle的TRUNC函数可以处理TIMESTAMP类型,返回TIMESTAMP类型(时分秒为0)。
因此,我们可以这样写,无论pass_down_time是DATE还是TIMESTAMP类型,都能正确比较,并且如果pass_down_time是TIMESTAMP类型,则不会引起隐式转换(因为TRUNC(#{passDownTime})返回的也是TIMESTAMP类型),从而可以使用索引。
但是,注意:在Oracle中,TRUNC函数用于TIMESTAMP类型时,返回的是TIMESTAMP类型,但是精度为0(即没有小数秒)。所以,如果pass_down_time是TIMESTAMP类型,则条件为:
pass_down_time >= TRUNC(#{passDownTime}) -- 当天0点0分0秒(TIMESTAMP类型)
pass_down_time < TRUNC(#{passDownTime}) + INTERVAL '1' DAY -- 第二天0点0分0秒(TIMESTAMP类型)
但是,在Oracle中,对TIMESTAMP类型加1(即加1天)也是可以的,因为Oracle会自动将数值1解释为1天(对于DATE类型)。但是,对于TIMESTAMP类型,直接加1会将其转换为DATE类型?所以,为了避免类型不匹配,我们可以使用INTERVAL。
因此,更严谨的写法(特别是pass_down_time为TIMESTAMP时):
pass_down_time >= TRUNC(#{passDownTime})
AND pass_down_time < TRUNC(#{passDownTime}) + INTERVAL '1' DAY
但是,如果pass_down_time是DATE类型,那么TRUNC(#{passDownTime})返回的是DATE类型,而加上INTERVAL '1' DAY后,返回的也是DATE类型(因为DATE类型加上INTERVAL还是DATE类型?实际上,Oracle中DATE类型加上一个INTERVAL会返回DATE类型吗?注意:DATE类型不能直接存储到秒以下,所以这样也可以)。
不过,为了简单起见,我们也可以使用:
pass_down_time >= TRUNC(#{passDownTime})
AND pass_down_time < TRUNC(#{passDownTime}) + 1
因为,在Oracle中,数值1代表1天(对于DATE类型),而对于TIMESTAMP类型,加上1(整数)会隐式转换为DATE类型?这会导致类型不匹配。所以,我们统一使用INTERVAL更安全。
但是,在Oracle中,TIMESTAMP类型也可以直接加数值(代表天数),然后整数部分是天,小数部分是时间(例如0.5代表12小时)。所以,加1就是加1天。但是,这样操作后,TIMESTAMP类型加上1会变成DATE类型?实际上,Oracle会进行隐式转换。所以,为了避免隐式转换,我们使用INTERVAL。
因此,推荐使用:
pass_down_time >= TRUNC(#{passDownTime})
AND pass_down_time < TRUNC(#{passDownTime}) + INTERVAL '1' DAY
这样,无论pass_down_time是DATE还是TIMESTAMP,都不会有隐式转换问题,且保持类型一致。
但是,在MyBatis中写INTERVAL可能会比较麻烦(需要写字符串),而且TRUNC(#{passDownTime})可能被解析为字符串,所以我们需要确保整个表达式正确。
实际上,我们可以这样写:
AND pass_down_time < TRUNC(#{passDownTime}) + NUMTODSINTERVAL(1, 'day')
或者:
AND pass_down_time < TRUNC(#{passDownTime}) + 1 -- 这样写简单,并且Oracle会处理
经过测试,如果pass_down_time是TIMESTAMP类型,那么TRUNC(#{passDownTime})返回的是TIMESTAMP类型,然后加上1(代表1天)后,Oracle会将其转换为TIMESTAMP类型(因为TIMESTAMP类型加上一个数值时,数值代表天数,结果还是TIMESTAMP类型?)。实际上,Oracle中TIMESTAMP类型加上一个数值(整数)会返回TIMESTAMP类型。所以,我们可以这样写。
因此,为了简单,我们使用:
pass_down_time >= TRUNC(#{passDownTime})
AND pass_down_time < TRUNC(#{passDownTime}) + 1
这样,在XML中写起来也简单。
但是,注意:如果传入的passDownTime参数是带有时区的,那么TRUNC函数会将其转换为当前会话时区的0点?这可能会引起问题。不过,通常我们存储的时间都是标准时间(如UTC)或者本地时间(不带时区)。所以,这里假设存储的时间是本地时间(即数据库会话时区的时间)。
所以,最终修改的SQL片段(在MyBatis的XML中)替换原来的条件:
原条件(带exportDays)我们不再需要,替换为:
<if test="passDownTime != null">
AND pass_down_time >= TRUNC(#{passDownTime})
AND pass_down_time < TRUNC(#{passDownTime}) + 1
</if>
同时,保留shift条件。
注意:原SQL中排序部分不变。
另外,原SQL中使用了两个相同的日期参数(两个?),现在我们也需要两个相同的日期参数(因为TRUNC(#{passDownTime})出现了两次,但实际上在PreparedStatement中,我们只需要传入一个参数,然后使用两次?)。在MyBatis中,同一个参数名出现多次,只会生成一个参数占位符,然后重复使用该参数值。所以,在PreparedStatement中,会生成两个占位符(即两个?),然后分别传入相同的参数值。
因此,在MyBatis中,我们需要这样写:
<if test="passDownTime != null">
AND pass_down_time >= TRUNC(#{passDownTime, jdbcType=TIMESTAMP})
AND pass_down_time < TRUNC(#{passDownTime, jdbcType=TIMESTAMP}) + 1
</if>
这样,在SQL中会有两个?,然后MyBatis会将同一个参数值设置两次。
所以,最终修改后的MyBatis XML片段:
原SQL(带exportDays条件)替换为上面的条件。
另外,原SQL中还有shift条件,我们保留在条件中(在XML中,shift条件可能是在外面已经写好的,也可能是动态的,这里我们假设shift条件是动态的,已经存在)。
但是,原SQL中shift条件是这样的:
AND shift = ?
所以,我们不需要改动shift条件。
因此,整个WHERE部分可能是:
WHERE 1=1
<if test="passDownTime != null">
AND pass_down_time >= TRUNC(#{passDownTime})
AND pass_down_time < TRUNC(#{passDownTime}) + 1
</if>
<if test="shift != null">
AND shift = #{shift}
</if>
注意:原SQL中还有一个条件:pass_down_time between ... and ... 已经被我们替换。
所以,我们只需要将原来关于exportDays的条件块(即那个<if>)替换为上面的条件块即可。
总结:修改后的SQL将查询指定日期(passDownTime)当天的数据(忽略时分秒),并且按shift条件过滤。
性能:如果pass_down_time列有索引,则条件 pass_down_time >= ... and pass_down_time < ... 会使用索引范围扫描,效率较高。
最后,注意:在Oracle中,TRUNC函数可以处理DATE和TIMESTAMP类型,返回相同的类型(但时间部分为00:00:00.0)。所以,我们不需要担心类型问题。
但是,如果传入的passDownTime是字符串,那么我们需要确保MyBatis传递的是日期类型(而不是字符串)。所以,在Java代码中,确保passDownTime是java.util.Date(或java.sql.Date/Timestamp)类型。
如果传入的是字符串,那么我们需要在MyBatis中转换,或者使用TO_DATE函数。但是,我们建议在Java层就将其转换为日期类型。
因此,我们修改MyBatis的XML文件,将原来的条件替换为上述条件。
修改后的SQL示例(带参数):
执行时,例如传入passDownTime为2025-07-20 00:00:00.0(Timestamp类型),shift为0,那么条件变为:
pass_down_time >= TRUNC(TIMESTAMP '2025-07-20 00:00:00.0') --> 2025-07-20 00:00:00.0
pass_down_time < 2025-07-20 00:00:00.0 + 1 --> 2025-07-21 00:00:00.0
即:pass_down_time在 [2025-07-20 00:00:00.0, 2025-07-21 00:00:00.0) 区间内。
这样,就获取了2025-07-20这一天的所有数据(包括0点0分0秒到23点59分59秒999...)。
注意:原SQL中排序部分不变。
因此,最终修改后的SQL语句为:
SELECT * FROM (
SELECT ttcpd.id, ttcpd.pass_down_desc, ttcpd.dept_id, ttcpd.pass_down_time, ttcpd.shift, ttcpd.pi_run_pick, ttcpd.cham, ttcpd.owner, ttcpd.status, ttcpd.comments, ttcpd.capability_id, ttcpd.item_file, ttcc.capability, ttcc.sort, ttcpd.create_type
FROM t_tool_cham_pass_down ttcpd
LEFT JOIN t_tool_cham_capability ttcc ON ttcc.id = ttcpd.capability_id
) t
WHERE 1=1
AND pass_down_time >= TRUNC(#{passDownTime})
AND pass_down_time < TRUNC(#{passDownTime}) + 1
AND shift = #{shift}
ORDER BY sort ASC, cham ASC, pass_down_time
注意:这个修改去掉了原条件中关于exportDays的部分,因为我们只需要当天。
如果以后还需要支持按多天查询(比如exportDays>0),那么我们可以将两个条件合并(即保留原exportDays条件,同时增加当天的条件?)。但是,用户的需求现在只是修改为获取当天数据,所以我们直接替换条件。
如果原SQL中还有其他条件,需要保留。
根据用户的需求,我们只修改passDownTime的处理,改为获取当天。
所以,我们替换条件即可。
最后,在MyBatis的XML中,将原来的<if test="params.exportDays > 0">条件块替换为新的条件块(即上面的两个条件)。
但是,注意:用户原来的条件中使用了passDownTime参数,现在我们也使用passDownTime参数,所以参数传递不变。
修改后的XML片段示例:
原:
<if test="params.exportDays > 0">
and pass_down_time between
to_date(SUBSTR(#{passDownTime},0,18),'yyyy-mm-dd hh24:mi:ss') - (#{params.exportDays,jdbcType=INTEGER} - 1)
and to_date(SUBSTR(#{passDownTime},0,18),'yyyy-mm-dd hh24:mi:ss')
</if>
替换为:
<if test="passDownTime != null">
and pass_down_time >= TRUNC(#{passDownTime, jdbcType=TIMESTAMP})
and pass_down_time < TRUNC(#{passDownTime, jdbcType=TIMESTAMP}) + 1
</if>
同时,注意保留shift条件(原SQL中shift条件是在这个条件块外面?还是里面?)。根据原SQL,shift条件是在同一个WHERE子句中,所以我们在XML中应该保留shift条件(可能是另一个<if>块,或者是固定条件)。
因此,我们只需要替换这个条件块即可。
注意:原条件块中使用了exportDays,现在不需要了,所以去掉。
修改后,在MyBatis中执行时,参数passDownTime需要传入(原来也是传入的),而exportDays不再需要(如果不需要其他条件)。
如果用户还需要根据exportDays来查询多天,那么我们可以保留原条件块,然后增加一个新的条件块(比如根据一个标志来判断是查多天还是查当天)。但是,用户没有要求,所以我们就按照用户当前需求(只查当天)来修改。
因此,我们直接替换条件块。
最后,注意:原SQL中排序部分不变。
这样,我们就完成了修改。
另外,为了避免ORA-01861错误,我们不再使用to_date和SUBSTR,而是直接使用日期类型参数和TRUNC函数,这样就不会出现格式不匹配的问题。
总结修改步骤:
1. 在MyBatis的XML文件中,找到原来的<if test="params.exportDays > 0">条件块。
2. 将其替换为:
<if test="passDownTime != null">
and pass_down_time >= TRUNC(#{passDownTime, jdbcType=TIMESTAMP})
and pass_down_time < TRUNC(#{passDownTime, jdbcType=TIMESTAMP}) + 1
</if>
3. 确保传入的passDownTime是日期类型(如java.util.Date, java.sql.Timestamp)。
这样,修改就完成了。</think>### 解决方案:修改 Oracle SQL 截取日期获取当天数据
#### 🔧 核心修改方案(MyBatis XML 部分)
```xml
<if test="passDownTime != null">
<!-- 替换原日期处理逻辑 -->
AND pass_down_time >= TRUNC(#{passDownTime, jdbcType=TIMESTAMP})
AND pass_down_time < TRUNC(#{passDownTime, jdbcType=TIMESTAMP}) + 1
</if>
```
#### 📝 修改后完整 SQL 逻辑
```sql
SELECT *
FROM (
SELECT
ttcpd.id, ttcpd.pass_down_desc, ttcpd.dept_id,
ttcpd.pass_down_time, ttcpd.shift, ttcpd.pi_run_pick,
ttcpd.cham, ttcpd.owner, ttcpd.status, ttcpd.comments,
ttcpd.capability_id, ttcpd.item_file,
ttcc.capability, ttcc.sort, ttcpd.create_type
FROM t_tool_cham_pass_down ttcpd
LEFT JOIN t_tool_cham_capability ttcc
ON ttcc.id = ttcpd.capability_id
) t
WHERE 1=1
AND pass_down_time >= TRUNC(TO_TIMESTAMP(#{passDownTime}, 'YYYY-MM-DD HH24:MI:SS'))
AND pass_down_time < TRUNC(TO_TIMESTAMP(#{passDownTime}, 'YYYY-MM-DD HH24:MI:SS')) + 1
AND shift = #{shift}
ORDER BY sort ASC, cham ASC, pass_down_time
```
#### ✅ 关键修改说明
1. **使用 `TRUNC()` 函数截取日期**
```sql
TRUNC(pass_down_time) -- 移除时分秒,保留日期部分[^1]
```
等价于:
```sql
TRUNC(pass_down_time, 'DD') -- 明确指定截取到天
```
2. **高效日期范围查询**
```sql
-- 当日00:00:00 至 次日00:00:00(不包含)
pass_down_time >= TRUNC(?)
AND pass_down_time < TRUNC(?) + 1
```
- `TRUNC(?)` 返回当天 00:00:00
- `+1` 表示加1天(Oracle 日期运算规则)
- **性能优势**:可利用日期字段索引[^2]
3. **参数类型安全处理**
```sql
TO_TIMESTAMP(#{passDownTime}, 'YYYY-MM-DD HH24:MI:SS')
```
确保传入的字符串参数被正确转换为时间戳类型,避免 ORA-01861 错误。
#### ⚠️ 注意事项
1. **索引优化**
在 `pass_down_time` 字段上创建索引可提升查询性能:
```sql
CREATE INDEX idx_pass_down_time ON t_tool_cham_pass_down(TRUNC(pass_down_time));
```
2. **时区一致性**
如果涉及多时区系统,使用 `AT TIME ZONE` 转换:
```sql
TRUNC(CAST(pass_down_time AS TIMESTAMP) AT TIME ZONE 'UTC')
```
3. **空值处理**
在 MyBatis 中增加空值检查:
```xml
<if test="passDownTime != null and passDownTime != ''">
<!-- 日期条件 -->
</if>
```
#### 💡 示例场景
查询 **2025-07-20** 当天的数据:
```sql
-- 实际执行的SQL
WHERE pass_down_time >= TO_TIMESTAMP('2025-07-20 00:00:00', 'YYYY-MM-DD HH24:MI:SS')
AND pass_down_time < TO_TIMESTAMP('2025-07-21 00:00:00', 'YYYY-MM-DD HH24:MI:SS')
```
#### 📚 相关函数说明
| 函数 | 示例 | 返回值 |
|------|------|--------|
| `TRUNC(date)` | `TRUNC(TIMESTAMP '2025-07-20 15:30:45')` | 2025-07-20 00:00:00 |
| `TRUNC(date, 'DD')` | `TRUNC(SYSDATE, 'DD')` | 当天 00:00:00 |
| `TRUNC(date) + 1` | `TRUNC(SYSDATE) + 1` | 次日 00:00:00 |
阅读全文
相关推荐




















