Oracle 数字区间、日期边界问题分类总结(日期运算、日期比较大小)

Oracle中不同函数对数字区间的处理方式不同:

  1. FOR循环:闭区间[m,n],包含结束值(如1..3包含3)
  2. SUBSTR :从起始位置开始截取指定长度字符(非结束位置),特殊规则:
    • 0视为第1位
    • 负数表示从末尾倒数
  3. INSTR:返回子串出现的位置(从1计数)
  4. 其他函数:
    • ROUND/TRUNC处理小数位
    • MOD取余数
    • POWER计算幂次

关键区别:

  • FOR循环关注起止值包含性
  • SUBSTR关注起始位置和长度
  • INSTR返回位置索引

日期处理:

  1. BETWEEN对日期包含结束日00:00:00,不包含时间部分
  2. 推荐使用>=开始 AND <结束日+1的方式确保包含完整时间段
  3. TRUNC函数去除时间部分,LAST_DAY获取月末
  4. 时间段查询应使用不等号确保边界完整

关键区别:数字区间关注起止值包含性,日期处理需特别注意时间部分的边界问题。


📊 Oracle 数字区间问题分类总结

功能/函数 语法 起始位置 是否包含结束位置 边界规则说明 示例 结果
FOR 循环 FOR i IN m..n LOOP 从 m 开始 包含 n 闭区间 [m, n],i 会取到 n FOR i IN 1..3 LOOP i = 1,2,3
SUBSTR 截取 SUBSTR(str, m, n) 从第 m 位开始 包含 截取 n 个字符,不是到第 n 位 SUBSTR('ABCDE', 2, 3) 'BCD'(位置2,3,4)
SUBSTR 简写 SUBSTR(str, m) 从第 m 位开始 包含 截取到字符串末尾 SUBSTR('ABCDE', 3) 'CDE'
INSTR 查找 INSTR(str, sub, m, n) 从第 m 位开始搜索 N/A(返回值是位置) 返回第 n 次出现的位置(从1计数) INSTR('ABABA', 'B', 1, 2) 4(第2个B的位置)
ROUND 四舍五入 ROUND(num, m) 第 m 位小数 N/A(不是区间问题) m 为正:小数点后;m 为负:小数点前 ROUND(123.456, 1) 123.5
TRUNC 截断 TRUNC(num, m) 第 m 位小数 N/A(不是区间问题) 直接截断,不四舍五入 TRUNC(123.456, 1) 123.4
MOD 取模 MOD(m, n) N/A N/A 返回 m 除以 n 的余数 MOD(10, 3) 1
POWER 幂运算 POWER(m, n) N/A N/A 返回 m 的 n 次方 POWER(2, 3) 8

🎯 容易混淆的重点对比

1️⃣ FOR 循环 vs SUBSTR 的"结束"概念

对比项 FOR 循环 SUBSTR
区间类型 位置区间 [m, n] 长度区间(从 m 开始取 n 个)
参数含义 结束 截取长度
是否包含 包含结束值 包含起始位置,取 n 个字符

sql

sql 复制代码
-- FOR 循环:包含 5
FOR i IN 1..5 LOOP  -- i = 1,2,3,4,5

-- SUBSTR:第2个参数是长度,不是结束位置
SUBSTR('ABCDE', 2, 3)  -- 从位置2开始取3个 → 'BCD'(位置2,3,4)

2️⃣ SUBSTR 的起始位置规则(特殊!)

起始位置 行为 示例(字符串 'ABCDE' 结果
0 视为第 1 位 SUBSTR('ABCDE', 0, 2) 'AB'
1 正常从第 1 位开始 SUBSTR('ABCDE', 1, 2) 'AB'
正数 n 从第 n 位开始 SUBSTR('ABCDE', 3, 2) 'CD'
负数 n 从末尾倒数第 n 位开始 SUBSTR('ABCDE', -2, 2) 'DE'

3️⃣ INSTR 的位置返回值

sql

sql 复制代码
-- INSTR 返回的是位置编号(从1开始)
INSTR('ABCDE', 'C')     -- 返回 3
INSTR('ABABA', 'B', 1, 2)  -- 从位置1开始找第2个B → 返回 4

✅ 快速记忆口诀

场景 口诀
FOR 循环 两点之间,两头都算
SUBSTR 起始位置 + 取几个,不是到哪
INSTR 找到后告诉你它在第几个位置
负数索引 SUBSTR 倒数开始,INSTR 不动

示例

sql 复制代码
--练习:
-- 1,开发函数 ELIM函数 实现功能 ELIM('ABBBBBCD','B') 返回
--   ELIM('ABBBBCD','B')
--   干掉 第一次出现 第二个入参的内容
--   ELIM('A-B-C','-')返回 'AB-C'
create or replace function ELIM(str varchar2,s varchar2)
return varchar2
is v_res varchar2(100);
    v_start number;
    begin
        if instr(str,s)=0
        then
            return str;
            else
            v_start:=instr(str,s,1,1);
            --拼接字符串
            v_res:=substr(str,1,v_start-1)||substr(str,v_start+1);
            dbms_output.put_line(v_res);
            return v_res;
        end if;
    end;

--单独执行 select 不要选中 自定义的函数 一起执行
select ELIM('A-B-C','-')as ELIM from dual;

📊 Oracle 日期边界问题分类总结


一、BETWEEN...AND 的日期边界

场景 语法示例 是否包含结束日期 实际等价条件 说明
日期类型(无时间) WHERE date_col BETWEEN DATE '2024-01-01' AND DATE '2024-01-31' 包含 date_col >= DATE '2024-01-01' AND date_col <= DATE '2024-01-31' 包含结束日期当天 00:00:00
日期类型(有时间) WHERE date_col BETWEEN DATE '2024-01-01' AND DATE '2024-01-31' ⚠️ 不包含31日的时间部分 实际只到 2024-01-31 00:00:00 常见陷阱2024-01-31 14:30:00 不会被查到
时间戳类型 WHERE ts_col BETWEEN TIMESTAMP '2024-01-01 00:00:00' AND TIMESTAMP '2024-01-31 23:59:59' 完全包含 明确指定了时间边界 需要精确到秒或毫秒

二、常见的日期边界处理方式

需求场景 错误写法(陷阱) 正确写法 说明
查询某一天的数据 WHERE date_col = DATE '2024-01-31' WHERE date_col >= DATE '2024-01-31' AND date_col < DATE '2024-02-01' 等于只查当天 00:00:00,漏掉有时间的数据
BETWEEN 查整月 WHERE date_col BETWEEN DATE '2024-01-01' AND DATE '2024-01-31' WHERE date_col >= DATE '2024-01-01' AND date_col < DATE '2024-02-01' BETWEEN 不包含1月31日的时间部分
查询本月至今 WHERE date_col BETWEEN TRUNC(SYSDATE, 'MM') AND SYSDATE WHERE date_col >= TRUNC(SYSDATE, 'MM') AND date_col < SYSDATE + 1 BETWEEN 可能漏掉今天的部分时间

三、日期函数边界规则

函数 语法 边界规则 示例(以 2024-01-31 14:30:45 为例) 结果
TRUNC TRUNC(date, 'unit') 截断到指定单位的开始 TRUNC(SYSDATE, 'MM') 2024-01-01 00:00:00
TRUNC TRUNC(date, 'DD') 截断到当天的开始 TRUNC(SYSDATE) 2024-01-31 00:00:00
LAST_DAY LAST_DAY(date) 返回月份最后一天的 00:00:00 LAST_DAY(DATE '2024-01-31') 2024-01-31 00:00:00
ADD_MONTHS ADD_MONTHS(date, n) 月份边界自动处理 ADD_MONTHS(DATE '2024-01-31', 1) 2024-02-29 00:00:00(闰年)
NEXT_DAY NEXT_DAY(date, 'weekday') 返回下一个指定星期几 NEXT_DAY(DATE '2024-01-31', 'MONDAY') 下一个周一 00:00:00

四、常见的时间段区间写法(最佳实践)

时间段 包含边界写法 说明
某一天 date_col >= DATE '2024-01-31' AND date_col < DATE '2024-02-01' ✅ 推荐:包含当天所有时间
某一月 date_col >= DATE '2024-01-01' AND date_col < DATE '2024-02-01' ✅ 推荐:包含整月
某一小时 date_col >= TIMESTAMP '2024-01-31 14:00:00' AND date_col < TIMESTAMP '2024-01-31 15:00:00' ✅ 包含14:00:00到14:59:59
本周 date_col >= TRUNC(SYSDATE, 'IW') AND date_col < TRUNC(SYSDATE, 'IW') + 7 周一到周日(ISO标准)
本月 date_col >= TRUNC(SYSDATE, 'MM') AND date_col < ADD_MONTHS(TRUNC(SYSDATE, 'MM'), 1) 整月
本季度 date_col >= TRUNC(SYSDATE, 'Q') AND date_col < ADD_MONTHS(TRUNC(SYSDATE, 'Q'), 3) 整季度
本年 date_col >= TRUNC(SYSDATE, 'YY') AND date_col < ADD_MONTHS(TRUNC(SYSDATE, 'YY'), 12) 整年

五、BETWEEN 与日期使用的具体示例

❌ 错误示例(常见陷阱)

sql

sql 复制代码
-- 查询 2024-01-31 的所有订单
SELECT * FROM orders 
WHERE order_date BETWEEN DATE '2024-01-31' AND DATE '2024-01-31';
-- 结果:只查出 order_date = 2024-01-31 00:00:00 的订单
-- 漏掉:2024-01-31 14:30:00 的订单

✅ 正确示例

sql

sql 复制代码
-- 方法1:使用 >= 和 <(推荐)
SELECT * FROM orders 
WHERE order_date >= DATE '2024-01-31' 
  AND order_date < DATE '2024-02-01';

-- 方法2:使用 TRUNC(但会索引失效)
SELECT * FROM orders 
WHERE TRUNC(order_date) = DATE '2024-01-31';

-- 方法3:精确到秒的 BETWEEN
SELECT * FROM orders 
WHERE order_date BETWEEN TIMESTAMP '2024-01-31 00:00:00' 
                     AND TIMESTAMP '2024-01-31 23:59:59';

六、日期边界核心要点速查表

问题 答案
BETWEEN 包含结束日期吗? ✅ 包含,但只包含结束日期的 00:00:00
如何包含某一天的所有时间? 使用 >= 当天 AND < 明天
如何包含某一整月? 使用 >= 当月1号 AND < 下月1号
DATE 类型有时间部分吗? ✅ 有!Oracle DATE 包含时分秒
TRUNC 作用是什么? 去掉时间部分,归零到 00:00:00
判断是否同一天? TRUNC(date1) = TRUNC(date2)

✅ 记忆口诀

BETWEEN 虽包含,只到零点不算妙
要查整天用不等,大于等于小于明天好
月份区间也同理,上月1号到下月1号
TRUNC 归零去时间,比较日期最可靠


Oracle中的日期运算


Oracle中常用日期运算的总结表格:

运算类型 示例/公式 说明 返回值类型
日期 ± 数字 SYSDATE + 1 当前日期加1天 DATE
日期 - 日期 DATE1 - DATE2 两个日期相差的天数(可含小数) NUMBER
月份加减 ADD_MONTHS(date, n) ADD_MONTHS(date, -n) 加/减 n 个月 DATE
月份差 MONTHS_BETWEEN(date1, date2) date1 - date2 的月数(可为小数) NUMBER
下一个星期几 NEXT_DAY(date, '星期一') NEXT_DAY(date, 'MON') 指定日期后的第一个星期几 DATE
月末日期 LAST_DAY(date) 返回所在月份的最后一天 DATE
日期截断 TRUNC(date, 'unit') 按单位(年/月/日/时/分等)截断 DATE
日期舍入 ROUND(date, 'unit') 按单位舍入(过半进到下一单位) DATE
提取部分值 EXTRACT(YEAR FROM date) EXTRACT(MONTH FROM date) EXTRACT(DAY FROM date) EXTRACT(HOUR FROM timestamp) 提取年/月/日/时/分/秒 NUMBER
时间差(细粒度) (date1 - date2) * 24 小时数 NUMBER
(date1 - date2) * 24 * 60 分钟数 NUMBER
(date1 - date2) * 24 * 3600 秒数 NUMBER
使用时间戳间隔 (timestamp1 - timestamp2) DAY TO SECOND 返回天、时、分、秒的间隔 INTERVAL
数值转日期 TO_DATE('2024-01-15', 'YYYY-MM-DD') 字符串转DATE类型 DATE
日期转字符串 TO_CHAR(date, 'YYYY-MM-DD HH24:MI:SS') DATE转格式化的字符串 VARCHAR2

常用日期单位(用于TRUNC/ROUND)

  • 'YEAR' / 'YYYY' / 'Y' --- 年初(1月1日)

  • 'MONTH' / 'MM' / 'MON' --- 月初(1日)

  • 'DAY' --- 周初(周日,依赖于NLS设置)

  • 'Q' --- 季初

  • 'HH24' / 'HH' --- 小时

  • 'MI' --- 分钟

示例

sql

sql 复制代码
-- 加3天
SELECT SYSDATE + 3 FROM DUAL;

-- 加2个月
SELECT ADD_MONTHS(SYSDATE, 2) FROM DUAL;

-- 年龄(月数)
SELECT MONTHS_BETWEEN(SYSDATE, birth_date)/12 FROM employees;

-- 当月最后一天
SELECT LAST_DAY(SYSDATE) FROM DUAL;

-- 本周一(周一作为一周起始)
SELECT TRUNC(SYSDATE, 'IW') FROM DUAL;

-- 两个时间戳的间隔
SELECT (TIMESTAMP '2024-01-15 10:30:00' - TIMESTAMP '2024-01-14 08:20:00') DAY TO SECOND FROM DUAL;

注:TRUNC 和间隔运算在DATE和TIMESTAMP上略有不同,DATE运算结果为NUMBER,TIMESTAMP运算结果为INTERVAL类型。


日期比较大小


在 Oracle 中,比较日期大小有多种方式,取决于你的具体需求。下面整理了几种常见的方法:


1. 基本比较运算符

直接使用 <, >, =, <=, >=, <> 比较 DATE 类型:

sql

sql 复制代码
-- 查询某天之后的记录
SELECT * FROM orders WHERE order_date > DATE '2024-01-01';

-- 查询某天之前的记录
SELECT * FROM orders WHERE order_date < SYSDATE;

-- 查询某天当天的记录
SELECT * FROM orders WHERE order_date = DATE '2024-03-15';

-- 注意:DATE 包含时间,等于判断需要精确到秒
SELECT * FROM orders 
WHERE order_date = TO_DATE('2024-03-15 14:30:00', 'YYYY-MM-DD HH24:MI:SS');

2. 忽略时间部分(只比较日期)

如果字段包含时间,只想比较年月日:

sql

sql 复制代码
-- 方法1:TRUNC 函数
SELECT * FROM orders WHERE TRUNC(order_date) = DATE '2024-03-15';

-- 方法2:TO_CHAR 格式化
SELECT * FROM orders 
WHERE TO_CHAR(order_date, 'YYYY-MM-DD') = '2024-03-15';

-- 方法3:日期范围(性能较好)
SELECT * FROM orders 
WHERE order_date >= DATE '2024-03-15' 
  AND order_date < DATE '2024-03-16';

3. 在查询条件中比较

sql

sql 复制代码
-- 获取过去7天的数据
SELECT * FROM orders 
WHERE order_date >= SYSDATE - 7;

-- 获取本月数据
SELECT * FROM orders 
WHERE order_date >= TRUNC(SYSDATE, 'MM')
  AND order_date < ADD_MONTHS(TRUNC(SYSDATE, 'MM'), 1);

-- 获取今年数据
SELECT * FROM orders 
WHERE EXTRACT(YEAR FROM order_date) = EXTRACT(YEAR FROM SYSDATE);

4. 在 CASE / DECODE 中比较

sql

sql 复制代码
SELECT 
  order_id,
  order_date,
  CASE 
    WHEN order_date > SYSDATE THEN 'Future'
    WHEN order_date = TRUNC(SYSDATE) THEN 'Today'
    ELSE 'Past'
  END AS status
FROM orders;

5. 注意事项

问题 说明 建议
Oracle DATE 包含年、月、日、时、分、秒 = 比较必须完全匹配 用范围查询或 TRUNC
索引使用 TRUNC(col) 会使索引失效 col BETWEEN start AND end
时区 TIMESTAMP WITH TIME ZONE 需转换 CASTAT TIME ZONE
空值 任何 NULL 比较返回 UNKNOWN 加上 IS NULL 检查

6. 完整示例

sql

sql 复制代码
-- 创建测试表
CREATE TABLE test_dates (
  id NUMBER,
  event_date DATE
);

INSERT INTO test_dates VALUES (1, DATE '2024-01-15');
INSERT INTO test_dates VALUES (2, TO_DATE('2024-03-20 08:30:00', 'YYYY-MM-DD HH24:MI:SS'));
INSERT INTO test_dates VALUES (3, SYSDATE);

-- 查询示例
SELECT * FROM test_dates WHERE event_date > DATE '2024-02-01';
SELECT * FROM test_dates WHERE TRUNC(event_date) = DATE '2024-01-15';
SELECT * FROM test_dates 
WHERE event_date BETWEEN DATE '2024-01-01' AND DATE '2024-01-31';
相关推荐
dishugj3 小时前
SAP HANA数据库文件目录说明
服务器·数据库·oracle
努力进修4 小时前
连接条件下推原理与实战:解决子查询、CTE大数据量性能问题
数据库·oracle
且去填词5 小时前
Go并发模式进阶:从Worker Pool到可取消任务调度器
数据库·oracle·golang
云边有个稻草人5 小时前
金仓 KingbaseES Pro*C 迁移指南:从 Oracle 平滑迁移
oracle·数据库迁移·kingbasees·金仓数据库·国产化适配·proc 迁移
jimy15 小时前
Oracle的always free oci实例,standard em2.1.micro,保活脚本
服务器·oracle
曹牧1 天前
Oracle:视图排序
数据库·oracle
文档搬运工1 天前
单独导出Oracle中的job
oracle
会编程的土豆1 天前
MySQL DDL(数据定义语言)总结
数据库·mysql·oracle
Irene19911 天前
Oracle SQL Developer:多租户架构(CDB/PDB)、表空间管理和用户权限管理
oracle